From c6b81000b493e868a71eaaeea74de6049de0b8d0 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 1 Sep 2015 16:47:51 -0400 Subject: [PATCH 001/101] Bug 1200563 - Remove duplicated pref. r=ted --HG-- extra : commitid : wTCC5CKnPb --- modules/libpref/init/all.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 728530ec174d..c6836365625d 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4288,10 +4288,6 @@ pref("layers.offmainthreadcomposition.frame-rate", -1); pref("layers.async-pan-zoom.enabled", true); #endif -#ifdef MOZ_WIDGET_UIKIT -pref("layers.async-pan-zoom.enabled", true); -#endif - #ifdef XP_MACOSX pref("layers.enable-tiles", true); pref("layers.tile-width", 512); From e5bf0242d274512f9c598d7c77330abe35302956 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 1 Sep 2015 16:47:51 -0400 Subject: [PATCH 002/101] Bug 1185747 part 1 - Use pref/meta-viewport tag instead of DOMWindowUtils to set the CSS viewport for mochitests. r=tn --HG-- extra : commitid : 4z3MImH4xGx --- dom/tests/mochitest/dom-level0/innerWidthHeight_script.html | 5 ----- .../mochitest/dom-level0/test_innerWidthHeight_script.html | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/dom/tests/mochitest/dom-level0/innerWidthHeight_script.html b/dom/tests/mochitest/dom-level0/innerWidthHeight_script.html index df29c2ea4e5b..7406086ad77e 100644 --- a/dom/tests/mochitest/dom-level0/innerWidthHeight_script.html +++ b/dom/tests/mochitest/dom-level0/innerWidthHeight_script.html @@ -6,11 +6,6 @@ From 3a4b8916d2f348fc5b9eb75feb45bde0825b1d57 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Tue, 1 Sep 2015 16:47:52 -0400 Subject: [PATCH 003/101] Bug 1185747 part 2 - Remove magical reftest harness properties and use standard meta-viewport tags instead. r=tn --HG-- extra : commitid : KYDd76jwpXj --- layout/reftests/bugs/593243-1.html | 2 +- layout/reftests/bugs/593243-2.html | 2 +- layout/reftests/bugs/reftest.list | 4 ++-- layout/reftests/reftest-sanity/reftest.list | 10 +++++----- .../reftest-sanity/test-bg-attachment-fixed-ref.html | 4 ++-- .../reftest-sanity/test-bg-attachment-fixed.html | 2 +- layout/reftests/reftest-sanity/test-displayport-2.html | 4 ++-- .../reftests/reftest-sanity/test-displayport-bg.html | 4 ++-- layout/reftests/reftest-sanity/test-pos-fixed-ref.html | 4 ++-- .../reftest-sanity/test-pos-fixed-transform-ref.html | 3 ++- .../reftest-sanity/test-pos-fixed-transform.html | 4 ++-- layout/reftests/reftest-sanity/test-pos-fixed.html | 4 ++-- layout/tools/reftest/README.txt | 7 ------- layout/tools/reftest/reftest-content.js | 7 ------- 14 files changed, 24 insertions(+), 37 deletions(-) diff --git a/layout/reftests/bugs/593243-1.html b/layout/reftests/bugs/593243-1.html index bb217438a1cd..c54d10b6fd49 100644 --- a/layout/reftests/bugs/593243-1.html +++ b/layout/reftests/bugs/593243-1.html @@ -1,10 +1,10 @@ + + + + + + + diff --git a/image/test/reftest/downscaling/png-interlaced.png b/image/test/reftest/downscaling/png-interlaced.png new file mode 100644 index 0000000000000000000000000000000000000000..a938d0bd6a93e72d5c381e2815123c720db31bcf GIT binary patch literal 806 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE#K6GVEq~V($YDu$^mSxlY+GRXVp=>%GT9@@ zmw};5je((|g@NH0P^jSr14F3+1H-EX1_rAc3=HB0b9M#V043OxyxmmfCPS#u0`IqM*LsgcCLRoI6B2G=f0T8 zFXeT?Q{;H4Aejc910=ju(uZFYd#^U`+JvL8_!|6^Syqec9AGcG5tsI?QjYWUjB9&Y zHTrX<=5$Z{^=?)`ciV=Z3q_L@>b5giFdeWwV9T(7>(bpBo+=MG8RQP-2kl>*wzMF4 zw*PUq>06t*t$o*V7Or_b`JTE-v}mK}q3eq|Hg}v|#yNM{<{4%aziwjme!1n4jgL}q zu-D_i<`a$_l-#tB|7qTR@wPqpSH7yhAMSl4_{aPfcC)`9{xAG2a4xh??6N)AzU#lg zKK&=*a(nv1yX_H2zLbSHY_#WFB+RtxgZIJv{)z>MBc8BdT+Cu;@3!D`%$Jf)i94ik zI(}Kf7j*2Ad@N8R#Z21Hv+d!#(vt~}3*YXy&n#WCF?rTY&K{rQvLADlS^e%E^kmtT z^J9r%0YwSWG*Z&Ke}9oWz3dA!IV&GgJlKW_g3k5JYxO%->Y z*F2OC7#SFv>Ka(+8d-!G7+4uuS{az@8kk!d7;M!$tBj%{H$Npa YtrE9}BgLijff^V*UHx3vIVCg!04+q4!T=1008) != downscale-2e.html?205,53,bottom about:blank +== downscale-png.html?16,16,interlaced downscale-png.html?16,16,normal +== downscale-png.html?24,24,interlaced downscale-png.html?24,24,normal + # RUN TESTS WITH HIGH QUALITY DOWNSCALING ENABLED: # ================================================ # High-quality downscaling enabled: @@ -149,3 +152,6 @@ fuzzy(20,999) != downscale-2c.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2d.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2e.html?205,53,bottom about:blank fuzzy(20,999) != downscale-2f.html?205,53,bottom about:blank + +== downscale-png.html?16,16,interlaced downscale-png.html?16,16,normal +== downscale-png.html?24,24,interlaced downscale-png.html?24,24,normal From dbd45351dc0e3ebf00c264daf1ea7b52357f1efd Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Wed, 2 Sep 2015 00:44:04 +0900 Subject: [PATCH 009/101] Bug 1195789 - Update fallback whitelist. r=cykesiopka --- .../manager/ssl/IntolerantFallbackList.inc | 69 ++----------------- security/manager/ssl/nsNSSIOLayer.cpp | 1 + 2 files changed, 6 insertions(+), 64 deletions(-) diff --git a/security/manager/ssl/IntolerantFallbackList.inc b/security/manager/ssl/IntolerantFallbackList.inc index e13b64df81c3..796d06592708 100644 --- a/security/manager/ssl/IntolerantFallbackList.inc +++ b/security/manager/ssl/IntolerantFallbackList.inc @@ -42,7 +42,6 @@ static const char* const kIntolerantFallbackList[] = "americanairlines.ie", // bug 1141604 "americanairlines.in", // bug 1141604 "americanairlines.jp", // bug 1141604 - "amss.mobilicity.ca", "ap.meitetsuunyu.co.jp", "apply.hkbn.net", // bug 1138451 "apps.amerch.com", @@ -50,12 +49,10 @@ static const char* const kIntolerantFallbackList[] = "apps.state.or.us", // bug 1130472 "appsrv.restat.com", "arcgames.com", // bug 1182932 - "ascii.jp", "asko.fi", // bug 1158584 "b2b.feib.com.tw", "baybloorradio.com", // bug 1173661 "beehive.miit.ru", - "bettertrades.com", "bgw.wangyin.com", // bug 1145521 "bianmin.chinapay.com", // bug 1137983 "bigflix.com", @@ -64,12 +61,10 @@ static const char* const kIntolerantFallbackList[] = "blogwatcher.co.jp", "bonds.euronext.com", // bug 1136091 "books.wwnorton.com", // bug 1116891 - "bredbandsbolaget.se", // bug 1158755 "bursar.ou.edu", "buttons.verticalresponse.com", "c2g.jupiter.fl.us", "canadaca.geotrust.com", // bug 1137677 - "car2go.com", // bug 1185080 "cbsfnotes1.blood.org.tw", "central.acadiau.ca", // bug 1152377 "cherry.de", // bug 1141521 @@ -78,12 +73,9 @@ static const char* const kIntolerantFallbackList[] = "club.guosen.com.cn", "coagov.aurora-il.org", "codem.codemasters.com", - "commerce.cashnet.com", // bug 1164009 "comune.milano.it", "corporbank.nbcb.com.cn", "crm.et2008.com", - "crossroads.schneider.com", - "cs.tokai-tv.com", "cualerts.dupaco.com", // bug 1116892 "customers.logistafrance.fr", // bug 1153951 "cwu.edu", @@ -100,7 +92,6 @@ static const char* const kIntolerantFallbackList[] = "ebank.hxb.com.cn", "ebank.hzbank.com.cn", "ebank.rcbcy.com", // bug 1146755 - "ebanking.ocbcwhhk.com", // bug 1141746 "ebill2.virginmedia.com", // bug 1129887 "ebpp.airtel.lk", "ebspay.boc.cn", // bug 1155567 @@ -114,7 +105,6 @@ static const char* const kIntolerantFallbackList[] = "epicreg.com", "eremit.sbising.com", "eservices.palomar.edu", - "essentialsupplies.com", "event.kasite.net", "extranet.eurocontrol.int", "ez.cityofchesapeake.net", @@ -133,19 +123,17 @@ static const char* const kIntolerantFallbackList[] = "giftcertificates.com", "hercle.com", "hpshop.gr", - "ibusiness.shacombank.com.hk", // bug 1141989 "identity.virginmedia.com", // bug 1129887 "ifund.allianzglobalinvestors.com.tw", "ig1.i-grasp.com", // bug 1167894 "ig4.i-grasp.com", // bug 1167894 "ihr.suburbanpropane.com", - "images.bankofthewest.com", // bug 1127204 "inside.i-med.ac.at", "its.bocmacau.com", "jbclick.jaxbchfl.net", // bug 1158465 - "jifenpay.com", "jst.doded.mil", // bug 1152627 "keirin.jp", + "king-solarman.com", // bug 1190706 "kjp.keinet.ne.jp", "kjp.oo.kawai-juku.ac.jp", "lewisham.gov.uk", @@ -153,47 +141,35 @@ static const char* const kIntolerantFallbackList[] = "login.chicagopolice.org", "login.ermis.gov.gr", "m.e-hon.ne.jp", - "m.safari.cwu.edu", // bug 1143035 "mail.izhnet.ru", "map.infonavit.org.mx", "mchrono.com", - "mecsumai.com", "member.edenredticket.com", "merchant.edenredticket.com", "meta-ehealth.com", "mobile.aa.com", // bug 1141604 "mobile.dream-prize.com", "mon-ulb.ulb.ac.be", - "my-csprd.ea.cwu.edu", // bug 1143035 - "my-csrenprd.ea.cwu.edu", // bug 1143035 - "my-fsprd.ea.cwu.edu", // bug 1143035 - "my-fsrenprd.ea.cwu.edu", // bug 1143035 - "my-fsrpt.ea.cwu.edu", // bug 1143035 - "my-hrprd.ea.cwu.edu", // bug 1143035 - "my-hrrenprd.ea.cwu.edu", // bug 1143035 - "my.cwu.edu", // bug 1143035 "my.if.com", // bug 1173592 "my.kyivstar.ua", "my.miit.ru", "myaccount.allstate.com", // bug 1143031 - "myaccount3.westnet.com.au", // bug 1157139 "mybank.nbcb.com.cn", "myhancock.hancockcollege.edu", - "myuws.uws.edu.au", "mywebreservations.com", "na.aiononline.com", // bug 1139782 "national.virginmedia.com", // bug 1129887 "nbank.hxb.com.cn", "netbanking.yesbank.co.in", // bug 1146090 - "niche.endsleigh.co.uk", + "new.fibi-online.co.il", // bug 1187242 "nmsmp.alsok.co.jp", "no1.nipponrentacar.co.jp", "obos1.obos.no", "officials.fhsaa.org", + "online.bankotsar.co.il", // bug 1187242 "online.newindia.co.in", "online.sainsburysbank.co.uk", "openwebosproject.org", // bug 1151990 - "opus.pinellascounty.org", "owa.byui.edu", "ozone.ou.edu", "parents.ou.edu", @@ -201,31 +177,25 @@ static const char* const kIntolerantFallbackList[] = "payment.condor.com", // bug 1152347 "payment.safepass.cn", "payments.virginmedia.com", // bug 1129887 - "poezd.rw.by", "portal.eztec.com.br", "portal.questonline.gr", "portal.uem.es", "profiles.uthscsa.edu", - "publicacionesoficiales.boe.es", "publicjobs.ie", "publicrecords.com", "racenet.codemasters.com", // bug 1163716 - "rapidscansecure.com", // bug 1177212 "recoup.com", "registration.o2.co.uk", - "regonline.com", // bug 1139783 "renewals.cipd.co.uk", "repair.kuroneko-kadendr.jp", // bug 1128366 "repairmb.kuroneko-kadendr.jp", // bug 1128366 "reputation.com", - "research-report.uws.edu.au", "reservations.usairways.com", // bug 1165400 "rietumu.lv", "roxyaffiliates.com", "sales.newchinalife.com", "sbank.hxb.com.cn", "sboseweb.mcpsweb.org", - "secure-checkout.t-mobile.com", // bug 1133648 "secure.bg-mania.jp", "secure.crbonline.gov.uk", // bug 1166644 "secure.fortisbc.com", @@ -241,27 +211,19 @@ static const char* const kIntolerantFallbackList[] = "shop.wildstar-online.com", // bug 1139782 "slovanet.sk", "smartcart.com", - "socialclub.rockstargames.com", // bug 1138673 "soeasy.sodexo.be", // bug 1117157 - "ss2.sfcollege.edu", "ss5.sfcollege.edu", "ssb.okbu.edu", // for port 8910, bug 1153749 "sso.acadiau.ca", // bug 1152377 - "starbucks.com", // bug 1167190 "stenhouse.com", - "store.moxa.com", - "svrch13.sugarlandtx.gov", "swdownloads.blackberry.com", // bug 1182997 "syzygy.co.uk", "tarjetacencosud.cl", "tele2.hr", - "tienda.boe.es", "tiendas.mediamarkt.es", "uralsg.megafon.ru", // bug 1153168 "usacycling.org", // bug 1163791 - "userdoor.com", "utradehub.or.kr", - "vod.skyperfectv.co.jp", "watch.sportsnet.ca", // bug 1144769 "web.asta.org", "webapps.ou.edu", @@ -280,8 +242,6 @@ static const char* const kIntolerantFallbackList[] = "www.aa.com.ve", // bug 1141604 "www.aavacations.com", // bug 1141604 "www.accessingram.com", - "www.acgov.org", - "www.acteonline.org", "www.aeroplan.com", // bug 1137543 "www.allbankonline.in", // bug 1156441 "www.allinpay.com", @@ -319,21 +279,17 @@ static const char* const kIntolerantFallbackList[] = "www.bauschonline.com", "www.baybloorradio.com", // bug 1173661 "www.bbsfonline.com", - "www.bettertrades.com", "www.bigflix.com", "www.blastam.com", "www.blogwatcher.co.jp", "www.blueprintonline.co.za", - "www.boe.es", "www.boostmobilesales.com", // bug 1112178 "www.borsaitaliana.it", "www.bottegaverde.es", "www.bottegaverde.it", "www.bottegaverde.pt", - "www.bredbandsbolaget.se", // bug 1158755 "www.businessdirect.bt.com", "www.cafedumonde.jp", - "www.car2go.com", // bug 1185080 "www.careers.asio.gov.au", "www.cherry.de", // bug 1141521 "www.chinapay.com", // bug 1137983 @@ -347,7 +303,7 @@ static const char* const kIntolerantFallbackList[] = "www.creditagricole.info", "www.css-club.net", "www.ctfeshop.com.cn", - "www.cwu.edu", + "www.cwu.edu", // bug 1143035 "www.dabs.com", "www.dabs.ie", "www.dabs4work.ie", @@ -364,21 +320,17 @@ static const char* const kIntolerantFallbackList[] = "www.ermis.gov.gr", "www.esadealumni.net", "www.esavingsaccount.co.uk", - "www.escrowrefills.com", - "www.essentialsupplies.com", "www.everyd.com", "www.ezpay.com.tw", "www.fhsaa.org", "www.fibi-online.co.il", // bug 1165580 "www.fj96336.com", - "www.fontainebleau.com", "www.foundersc.com", "www.fubar.com", "www.gamers-onlineshop.jp", // bug 1126654 "www.gbe-bund.de", "www.giftcertificates.com", "www.gtja.com", - "www.haynes.co.uk", "www.hercle.com", "www.hn.10086.cn", "www.hpshop.gr", @@ -388,20 +340,18 @@ static const char* const kIntolerantFallbackList[] = "www.interpark.com", "www.isracard.co.il", // bug 1165582 "www.jaf.or.jp", - "www.jifenpay.com", "www.kasite.net", "www.khan.co.kr", + "www.king-solarman.com", // bug 1190706 "www.kredodirect.com.ua", // bug 1095507 "www.law888.com.tw", "www.lewisham.gov.uk", - "www.lib.cwu.edu", "www.libraryvideo.com", "www.lm-order.de", "www.londonstockexchange.com", "www.matkahuolto.info", "www.matrics.or.jp", "www.mchrono.com", - "www.mecsumai.com", "www.meta-ehealth.com", "www.misterdonut.jp", "www.mp2.aeroport.fr", @@ -409,12 +359,10 @@ static const char* const kIntolerantFallbackList[] = "www.mtsindia.in", // RC4 "www.my.airdo.jp", // bug 1129773 "www.myagent.gov.ab.ca", // bug 1152827 - "www.mynpcdata.net", "www.mywebreservations.com", "www.ncsoft.com", // bug 1139782 "www.nec-nexs.com", "www.newchinalife.com", - "www.nishi.or.jp", "www.ocbcwhhk.com", // bug 1141746 "www.openwebosproject.org", // bug 1151990 "www.pen-kanagawa.ed.jp", @@ -422,15 +370,12 @@ static const char* const kIntolerantFallbackList[] = "www.publicjobs.ie", "www.publicrecords.com", "www.pwcrecruiting.com", - "www.rapidscansecure.com", // bug 1177212 "www.razorgator.com", "www.recoup.com", - "www.regonline.com", // bug 1139783 "www.renaultcredit.com.ar", "www.reputation.com", "www.rietumu.lv", "www.rimac.com.pe", - "www.riversendtrading.com", "www.roxyaffiliates.com", "www.s-book.net", "www.safepass.cn", @@ -439,9 +384,7 @@ static const char* const kIntolerantFallbackList[] = "www.shop.bt.com", "www.slovanet.sk", "www.smartcart.com", - "www.smartoffice.jp", "www.sokamocka.com", - "www.starbucks.com", // bug 1167190 "www.stenhouse.com", "www.sunderland.gov.uk", "www.syzygy.co.uk", @@ -455,7 +398,6 @@ static const char* const kIntolerantFallbackList[] = "www.ur-net.go.jp", "www.usacycling.org", // bug 1163791 "www.usairways.com", // bug 1142703 - "www.userdoor.com", "www.utradehub.or.kr", "www.virgin.net", "www.wavecable.com", @@ -471,7 +413,6 @@ static const char* const kIntolerantFallbackList[] = "www3.ibac.co.jp", "www3.taiheiyo-ferry.co.jp", "www4.aeroplan.com", // bug 1137543 - "wwws.kadokawa.co.jp", "xyk.cebbank.com", // bug 1145524 "zenfolio.com", "zoominfo.com", diff --git a/security/manager/ssl/nsNSSIOLayer.cpp b/security/manager/ssl/nsNSSIOLayer.cpp index 7b45843eb16d..4779b7c579e6 100644 --- a/security/manager/ssl/nsNSSIOLayer.cpp +++ b/security/manager/ssl/nsNSSIOLayer.cpp @@ -1723,6 +1723,7 @@ private: static const char* const kFallbackWildcardList[] = { + ".eur.xerox.com", // bug 1187215 ".kuronekoyamato.co.jp", // bug 1128366 ".wildcard.test", }; From e0121eceb5c2794b3d1a4b8aa2484c13ef56c434 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Wed, 2 Sep 2015 00:57:57 +0300 Subject: [PATCH 010/101] Bug 1199785 - Make atk methods in nsMaiInterfaceComponent.cpp to work with ipc proxies, r=tbsaunde --- accessible/atk/AccessibleWrap.cpp | 16 +-- accessible/atk/AtkSocketAccessible.cpp | 33 +----- accessible/atk/InterfaceInitFuncs.h | 4 +- accessible/atk/nsMai.h | 30 ++++++ accessible/atk/nsMaiInterfaceComponent.cpp | 115 ++++++++++++++------- accessible/ipc/DocAccessibleChild.cpp | 102 +++++++++++------- accessible/ipc/DocAccessibleChild.h | 24 +++-- accessible/ipc/PDocAccessible.ipdl | 9 +- accessible/ipc/ProxyAccessible.cpp | 30 +++++- accessible/ipc/ProxyAccessible.h | 6 ++ 10 files changed, 239 insertions(+), 130 deletions(-) diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index e379b371370e..4158da5d4863 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -1042,14 +1042,17 @@ refRelationSetCB(AtkObject *aAtkObj) AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj) { - NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr); + bool isMAIObject = IS_MAI_OBJECT(aAtkObj); + NS_ENSURE_TRUE(isMAIObject || MAI_IS_ATK_SOCKET(aAtkObj), + nullptr); - // Make sure its native is an AccessibleWrap not a proxy. - if (MAI_ATK_OBJECT(aAtkObj)->accWrap & IS_PROXY) + uintptr_t accWrapPtr = isMAIObject ? + MAI_ATK_OBJECT(aAtkObj)->accWrap : + reinterpret_cast(MAI_ATK_SOCKET(aAtkObj)->accWrap); + if (accWrapPtr & IS_PROXY) return nullptr; - AccessibleWrap* accWrap = - reinterpret_cast(MAI_ATK_OBJECT(aAtkObj)->accWrap); + AccessibleWrap* accWrap = reinterpret_cast(accWrapPtr); // Check if the accessible was deconstructed. if (!accWrap) @@ -1067,7 +1070,8 @@ GetAccessibleWrap(AtkObject* aAtkObj) ProxyAccessible* GetProxy(AtkObject* aObj) { - if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY)) + if (!aObj || !IS_MAI_OBJECT(aObj) || + !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY)) return nullptr; return reinterpret_cast(MAI_ATK_OBJECT(aObj)->accWrap diff --git a/accessible/atk/AtkSocketAccessible.cpp b/accessible/atk/AtkSocketAccessible.cpp index 7c049b94ca3f..95c3075e8048 100644 --- a/accessible/atk/AtkSocketAccessible.cpp +++ b/accessible/atk/AtkSocketAccessible.cpp @@ -21,35 +21,6 @@ const char* AtkSocketAccessible::sATKSocketGetTypeSymbol = "atk_socket_get_type" bool AtkSocketAccessible::gCanEmbed = FALSE; extern "C" void mai_atk_component_iface_init(AtkComponentIface* aIface); -extern "C" GType mai_atk_socket_get_type(void); - -/* MaiAtkSocket */ - -#define MAI_TYPE_ATK_SOCKET (mai_atk_socket_get_type ()) -#define MAI_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ - MAI_TYPE_ATK_SOCKET, MaiAtkSocket)) -#define MAI_IS_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ - MAI_TYPE_ATK_SOCKET)) -#define MAI_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ - MAI_TYPE_ATK_SOCKET,\ - MaiAtkSocketClass)) -#define MAI_IS_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ - MAI_TYPE_ATK_SOCKET)) -#define MAI_ATK_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ - MAI_TYPE_ATK_SOCKET,\ - MaiAtkSocketClass)) - -typedef struct _MaiAtkSocket -{ - AtkSocket parent; - - AccessibleWrap* accWrap; -} MaiAtkSocket; - -typedef struct _MaiAtkSocketClass -{ - AtkSocketClass parent_class; -} MaiAtkSocketClass; G_DEFINE_TYPE_EXTENDED(MaiAtkSocket, mai_atk_socket, AtkSocketAccessible::g_atk_socket_type, 0, @@ -86,7 +57,7 @@ RefAccessibleAtPoint(AtkComponent* aComponent, gint aX, gint aY, { NS_ENSURE_TRUE(MAI_IS_ATK_SOCKET(aComponent), nullptr); - return refAccessibleAtPointHelper(MAI_ATK_SOCKET(aComponent)->accWrap, + return refAccessibleAtPointHelper(ATK_OBJECT(MAI_ATK_SOCKET(aComponent)), aX, aY, aCoordType); } @@ -99,7 +70,7 @@ GetExtents(AtkComponent* aComponent, gint* aX, gint* aY, gint* aWidth, if (!MAI_IS_ATK_SOCKET(aComponent)) return; - getExtentsHelper(MAI_ATK_SOCKET(aComponent)->accWrap, + getExtentsHelper(ATK_OBJECT(MAI_ATK_SOCKET(aComponent)), aX, aY, aWidth, aHeight, aCoordType); } } diff --git a/accessible/atk/InterfaceInitFuncs.h b/accessible/atk/InterfaceInitFuncs.h index 805001e8222d..4714018cc427 100644 --- a/accessible/atk/InterfaceInitFuncs.h +++ b/accessible/atk/InterfaceInitFuncs.h @@ -34,9 +34,9 @@ void valueInterfaceInitCB(AtkValueIface *aIface); /** * XXX these should live in a file of utils for atk. */ -AtkObject* refAccessibleAtPointHelper(mozilla::a11y::AccessibleWrap* aAccWrap, +AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY, AtkCoordType aCoordType); -void getExtentsHelper(mozilla::a11y::AccessibleWrap* aAccWrap, +void getExtentsHelper(AtkObject* aAtkObj, gint* aX, gint* aY, gint* aWidth, gint* aHeight, AtkCoordType aCoordType); diff --git a/accessible/atk/nsMai.h b/accessible/atk/nsMai.h index aba96a4b38c7..4618524f7cf1 100644 --- a/accessible/atk/nsMai.h +++ b/accessible/atk/nsMai.h @@ -34,6 +34,36 @@ class ProxyAccessible; MaiAtkObjectClass)) GType mai_atk_object_get_type(void); GType mai_util_get_type(); +extern "C" GType mai_atk_socket_get_type(void); + +/* MaiAtkSocket */ + +#define MAI_TYPE_ATK_SOCKET (mai_atk_socket_get_type ()) +#define MAI_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\ + MAI_TYPE_ATK_SOCKET, MaiAtkSocket)) +#define MAI_IS_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\ + MAI_TYPE_ATK_SOCKET)) +#define MAI_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\ + MAI_TYPE_ATK_SOCKET,\ + MaiAtkSocketClass)) +#define MAI_IS_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + MAI_TYPE_ATK_SOCKET)) +#define MAI_ATK_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + MAI_TYPE_ATK_SOCKET,\ + MaiAtkSocketClass)) + +typedef struct _MaiAtkSocket +{ + AtkSocket parent; + + mozilla::a11y::AccessibleWrap* accWrap; +} MaiAtkSocket; + +typedef struct _MaiAtkSocketClass +{ + AtkSocketClass parent_class; +} MaiAtkSocketClass; + mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj); mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj); AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy); diff --git a/accessible/atk/nsMaiInterfaceComponent.cpp b/accessible/atk/nsMaiInterfaceComponent.cpp index 6a925e70ddb9..efd8eb65cd2b 100644 --- a/accessible/atk/nsMaiInterfaceComponent.cpp +++ b/accessible/atk/nsMaiInterfaceComponent.cpp @@ -11,6 +11,7 @@ #include "nsCoreUtils.h" #include "nsMai.h" #include "mozilla/Likely.h" +#include "mozilla/a11y/ProxyAccessible.h" using namespace mozilla::a11y; @@ -20,7 +21,7 @@ static AtkObject* refAccessibleAtPointCB(AtkComponent* aComponent, gint aAccX, gint aAccY, AtkCoordType aCoordType) { - return refAccessibleAtPointHelper(GetAccessibleWrap(ATK_OBJECT(aComponent)), + return refAccessibleAtPointHelper(ATK_OBJECT(aComponent), aAccX, aAccY, aCoordType); } @@ -28,73 +29,109 @@ static void getExtentsCB(AtkComponent* aComponent, gint* aX, gint* aY, gint* aWidth, gint* aHeight, AtkCoordType aCoordType) { - getExtentsHelper(GetAccessibleWrap(ATK_OBJECT(aComponent)), + getExtentsHelper(ATK_OBJECT(aComponent), aX, aY, aWidth, aHeight, aCoordType); } static gboolean grabFocusCB(AtkComponent* aComponent) { - AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aComponent)); - if (!accWrap) - return FALSE; + AtkObject* atkObject = ATK_OBJECT(aComponent); + AccessibleWrap* accWrap = GetAccessibleWrap(atkObject); + if (accWrap) { + accWrap->TakeFocus(); + return TRUE; + } - accWrap->TakeFocus(); - return TRUE; + ProxyAccessible* proxy = GetProxy(atkObject); + if (proxy) { + proxy->TakeFocus(); + return TRUE; + } + + return FALSE; } } AtkObject* -refAccessibleAtPointHelper(AccessibleWrap* aAccWrap, gint aX, gint aY, +refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY, AtkCoordType aCoordType) { - if (!aAccWrap || aAccWrap->IsDefunct() || nsAccUtils::MustPrune(aAccWrap)) - return nullptr; + AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); + if (accWrap) { + if (accWrap->IsDefunct() || nsAccUtils::MustPrune(accWrap)) { + return nullptr; + } - // Accessible::ChildAtPoint(x,y) is in screen pixels. - if (aCoordType == ATK_XY_WINDOW) { - nsIntPoint winCoords = - nsCoreUtils::GetScreenCoordsForWindow(aAccWrap->GetNode()); - aX += winCoords.x; - aY += winCoords.y; + // Accessible::ChildAtPoint(x,y) is in screen pixels. + if (aCoordType == ATK_XY_WINDOW) { + nsIntPoint winCoords = + nsCoreUtils::GetScreenCoordsForWindow(accWrap->GetNode()); + aX += winCoords.x; + aY += winCoords.y; + } + + Accessible* accAtPoint = accWrap->ChildAtPoint(aX, aY, + Accessible::eDirectChild); + if (!accAtPoint) { + return nullptr; + } + + AtkObject* atkObj = AccessibleWrap::GetAtkObject(accAtPoint); + if (atkObj) { + g_object_ref(atkObj); + } + + return atkObj; } - Accessible* accAtPoint = aAccWrap->ChildAtPoint(aX, aY, - Accessible::eDirectChild); - if (!accAtPoint) - return nullptr; + if (ProxyAccessible* proxy = GetProxy(aAtkObj)) { + ProxyAccessible* result = + proxy->AccessibleAtPoint(aX, aY, aCoordType == ATK_XY_WINDOW); + AtkObject* atkObj = result ? GetWrapperFor(result) : nullptr; + if (atkObj) { + g_object_ref(atkObj); + } + return atkObj; + } - AtkObject* atkObj = AccessibleWrap::GetAtkObject(accAtPoint); - if (atkObj) - g_object_ref(atkObj); - return atkObj; + return nullptr; } void -getExtentsHelper(AccessibleWrap* aAccWrap, +getExtentsHelper(AtkObject* aAtkObj, gint* aX, gint* aY, gint* aWidth, gint* aHeight, AtkCoordType aCoordType) { + AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); *aX = *aY = *aWidth = *aHeight = 0; - if (!aAccWrap || aAccWrap->IsDefunct()) - return; + if (accWrap) { + if (accWrap->IsDefunct()) { + return; + } - nsIntRect screenRect = aAccWrap->Bounds(); - if (screenRect.IsEmpty()) - return; + nsIntRect screenRect = accWrap->Bounds(); + if (screenRect.IsEmpty()) + return; - if (aCoordType == ATK_XY_WINDOW) { - nsIntPoint winCoords = - nsCoreUtils::GetScreenCoordsForWindow(aAccWrap->GetNode()); - screenRect.x -= winCoords.x; - screenRect.y -= winCoords.y; + if (aCoordType == ATK_XY_WINDOW) { + nsIntPoint winCoords = + nsCoreUtils::GetScreenCoordsForWindow(accWrap->GetNode()); + screenRect.x -= winCoords.x; + screenRect.y -= winCoords.y; + } + + *aX = screenRect.x; + *aY = screenRect.y; + *aWidth = screenRect.width; + *aHeight = screenRect.height; + return; } - *aX = screenRect.x; - *aY = screenRect.y; - *aWidth = screenRect.width; - *aHeight = screenRect.height; + if (ProxyAccessible* proxy = GetProxy(aAtkObj)) { + proxy->Extents(aCoordType == ATK_XY_WINDOW, aX, aY, aWidth, aHeight); + } } void diff --git a/accessible/ipc/DocAccessibleChild.cpp b/accessible/ipc/DocAccessibleChild.cpp index 98677f8085ae..0b1b010ef2f2 100644 --- a/accessible/ipc/DocAccessibleChild.cpp +++ b/accessible/ipc/DocAccessibleChild.cpp @@ -1792,42 +1792,6 @@ DocAccessibleChild::RecvFocusedChild(const uint64_t& aID, return true; } -bool -DocAccessibleChild::RecvChildAtPoint(const uint64_t& aID, - const int32_t& aX, - const int32_t& aY, - const uint32_t& aWhich, - uint64_t* aChild, - bool* aOk) -{ - *aChild = 0; - *aOk = false; - Accessible* acc = IdToAccessible(aID); - if (acc && !acc->IsDefunct() && !nsAccUtils::MustPrune(acc)) { - Accessible* child = - acc->ChildAtPoint(aX, aY, - static_cast(aWhich)); - if (child) { - *aChild = reinterpret_cast(child->UniqueID()); - *aOk = true; - } - } - - return true; -} - -bool -DocAccessibleChild::RecvBounds(const uint64_t& aID, - nsIntRect* aRect) -{ - Accessible* acc = IdToAccessible(aID); - if (acc && !acc->IsDefunct()) { - *aRect = acc->Bounds(); - } - - return false; -} - bool DocAccessibleChild::RecvLanguage(const uint64_t& aID, nsString* aLocale) @@ -1906,5 +1870,71 @@ DocAccessibleChild::RecvURLDocTypeMimeType(const uint64_t& aID, return true; } +bool +DocAccessibleChild::RecvAccessibleAtPoint(const uint64_t& aID, + const int32_t& aX, + const int32_t& aY, + const bool& aNeedsScreenCoords, + const uint32_t& aWhich, + uint64_t* aResult, + bool* aOk) +{ + *aResult = 0; + *aOk = false; + Accessible* acc = IdToAccessible(aID); + if (acc && !acc->IsDefunct() && !nsAccUtils::MustPrune(acc)) { + int32_t x = aX; + int32_t y = aY; + if (aNeedsScreenCoords) { + nsIntPoint winCoords = + nsCoreUtils::GetScreenCoordsForWindow(acc->GetNode()); + x += winCoords.x; + y += winCoords.y; + } + + Accessible* result = + acc->ChildAtPoint(x, y, + static_cast(aWhich)); + if (result) { + *aResult = reinterpret_cast(result->UniqueID()); + *aOk = true; + } + } + + return true; +} + +bool +DocAccessibleChild::RecvExtents(const uint64_t& aID, + const bool& aNeedsScreenCoords, + int32_t* aX, + int32_t* aY, + int32_t* aWidth, + int32_t* aHeight) +{ + *aX = 0; + *aY = 0; + *aWidth = 0; + *aHeight = 0; + Accessible* acc = IdToAccessible(aID); + if (acc && !acc->IsDefunct() && !nsAccUtils::MustPrune(acc)) { + nsIntRect screenRect = acc->Bounds(); + if (!screenRect.IsEmpty()) { + if (aNeedsScreenCoords) { + nsIntPoint winCoords = + nsCoreUtils::GetScreenCoordsForWindow(acc->GetNode()); + screenRect.x -= winCoords.x; + screenRect.y -= winCoords.y; + } + + *aX = screenRect.x; + *aY = screenRect.y; + *aWidth = screenRect.width; + *aHeight = screenRect.height; + } + } + return true; +} + } } diff --git a/accessible/ipc/DocAccessibleChild.h b/accessible/ipc/DocAccessibleChild.h index 736195355cb3..b85c354892b0 100644 --- a/accessible/ipc/DocAccessibleChild.h +++ b/accessible/ipc/DocAccessibleChild.h @@ -448,15 +448,6 @@ public: uint64_t* aChild, bool* aOk) override; - virtual bool RecvChildAtPoint(const uint64_t& aID, - const int32_t& aX, - const int32_t& aY, - const uint32_t& aWhich, - uint64_t* aChild, - bool* aOk) override; - - virtual bool RecvBounds(const uint64_t& aID, nsIntRect* aRect) override; - virtual bool RecvLanguage(const uint64_t& aID, nsString* aLocale) override; virtual bool RecvDocType(const uint64_t& aID, nsString* aType) override; virtual bool RecvTitle(const uint64_t& aID, nsString* aTitle) override; @@ -466,6 +457,21 @@ public: nsString* aURL, nsString* aDocType, nsString* aMimeType) override; + + virtual bool RecvAccessibleAtPoint(const uint64_t& aID, + const int32_t& aX, + const int32_t& aY, + const bool& aNeedsScreenCoords, + const uint32_t& aWhich, + uint64_t* aResult, + bool* aOk) override; + + virtual bool RecvExtents(const uint64_t& aID, + const bool& aNeedsScreenCoords, + int32_t* aX, + int32_t* aY, + int32_t* aWidth, + int32_t* aHeight) override; private: Accessible* IdToAccessible(const uint64_t& aID) const; diff --git a/accessible/ipc/PDocAccessible.ipdl b/accessible/ipc/PDocAccessible.ipdl index 9120366a543c..8938aa28038a 100644 --- a/accessible/ipc/PDocAccessible.ipdl +++ b/accessible/ipc/PDocAccessible.ipdl @@ -233,9 +233,6 @@ child: returns(uint64_t aChild); prio(high) sync FocusedChild(uint64_t aID) returns(uint64_t aChild, bool aOk); - prio(high) sync ChildAtPoint(uint64_t aID, int32_t aX, int32_t aY, uint32_t aWhich) - returns(uint64_t aChild, bool aOk); - prio(high) sync Bounds(uint64_t aID) returns(nsIntRect aRect); prio(high) sync Language(uint64_t aID) returns(nsString aLocale); prio(high) sync DocType(uint64_t aID) returns(nsString aType); @@ -243,6 +240,12 @@ child: prio(high) sync URL(uint64_t aID) returns(nsString aURL); prio(high) sync MimeType(uint64_t aID) returns(nsString aMime); prio(high) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType); + + prio(high) sync AccessibleAtPoint(uint64_t aID, int32_t aX, int32_t aY, bool aNeedsScreenCoords, uint32_t aWhich) + returns(uint64_t aResult, bool aOk); + + prio(high) sync Extents(uint64_t aID, bool aNeedsScreenCoords) + returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight); }; } diff --git a/accessible/ipc/ProxyAccessible.cpp b/accessible/ipc/ProxyAccessible.cpp index 4171925cf1f0..8c7fd97a6031 100644 --- a/accessible/ipc/ProxyAccessible.cpp +++ b/accessible/ipc/ProxyAccessible.cpp @@ -1019,9 +1019,9 @@ ProxyAccessible::ChildAtPoint(int32_t aX, int32_t aY, { uint64_t childID = 0; bool ok = false; - unused << mDoc->SendChildAtPoint(mID, aX, aY, - static_cast(aWhichChild), - &childID, &ok); + unused << mDoc->SendAccessibleAtPoint(mID, aX, aY, false, + static_cast(aWhichChild), + &childID, &ok); return ok ? mDoc->GetAccessible(childID) : nullptr; } @@ -1029,7 +1029,9 @@ nsIntRect ProxyAccessible::Bounds() { nsIntRect rect; - unused << mDoc->SendBounds(mID, &rect); + unused << mDoc->SendExtents(mID, false, + &(rect.x), &(rect.y), + &(rect.width), &(rect.height)); return rect; } @@ -1070,6 +1072,26 @@ ProxyAccessible::URLDocTypeMimeType(nsString& aURL, nsString& aDocType, unused << mDoc->SendURLDocTypeMimeType(mID, &aURL, &aDocType, &aMimeType); } +ProxyAccessible* +ProxyAccessible::AccessibleAtPoint(int32_t aX, int32_t aY, + bool aNeedsScreenCoords) +{ + uint64_t childID = 0; + bool ok = false; + unused << + mDoc->SendAccessibleAtPoint(mID, aX, aY, aNeedsScreenCoords, + static_cast(Accessible::eDirectChild), + &childID, &ok); + return ok ? mDoc->GetAccessible(childID) : nullptr; +} + +void +ProxyAccessible::Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY, + int32_t* aWidth, int32_t* aHeight) +{ + unused << mDoc->SendExtents(mID, aNeedsScreenCoords, aX, aY, aWidth, aHeight); +} + Accessible* ProxyAccessible::OuterDocOfRemoteBrowser() const { diff --git a/accessible/ipc/ProxyAccessible.h b/accessible/ipc/ProxyAccessible.h index 4dc401360fe9..efd197417a6b 100644 --- a/accessible/ipc/ProxyAccessible.h +++ b/accessible/ipc/ProxyAccessible.h @@ -319,6 +319,12 @@ public: void URLDocTypeMimeType(nsString& aURL, nsString& aDocType, nsString& aMimeType); + ProxyAccessible* AccessibleAtPoint(int32_t aX, int32_t aY, + bool aNeedsScreenCoords); + + void Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY, + int32_t* aWidth, int32_t* aHeight); + /** * Allow the platform to store a pointers worth of data on us. */ From 5cb124397c0465e45a40bf09884071147bb12364 Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Mon, 31 Aug 2015 17:59:31 -0400 Subject: [PATCH 011/101] Bug 1200413 - Part 1: Re-write RefCountedInsideLambdaChecker to use captures instead of checking for DeclRef instances, r=ehsan --- build/clang-plugin/clang-plugin.cpp | 24 ++++++++++--------- .../tests/TestNoRefcountedInsideLambdas.cpp | 8 +++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp index 899497ffc095..46bdcafd5201 100644 --- a/build/clang-plugin/clang-plugin.cpp +++ b/build/clang-plugin/clang-plugin.cpp @@ -1019,13 +1019,7 @@ DiagnosticsMatcher::DiagnosticsMatcher() { // lambda, where the declaration they reference is not inside the lambda. // This excludes arguments and local variables, leaving only captured // variables. - astMatcher.addMatcher( - lambdaExpr(hasDescendant( - declRefExpr(hasType(pointerType(pointee(isRefCounted()))), - to(decl().bind("decl"))) - .bind("declref")), - unless(hasDescendant(decl(equalsBoundNode("decl"))))), - &refCountedInsideLambdaChecker); + astMatcher.addMatcher(lambdaExpr().bind("lambda"), &refCountedInsideLambdaChecker); // Older clang versions such as the ones used on the infra recognize these // conversions as 'operator _Bool', but newer clang versions recognize these @@ -1252,11 +1246,19 @@ void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run( "Refcounted variable %0 of type %1 cannot be captured by a lambda"); unsigned noteID = Diag.getDiagnosticIDs()->getCustomDiagID( DiagnosticIDs::Note, "Please consider using a smart pointer"); - const DeclRefExpr *declref = Result.Nodes.getNodeAs("declref"); + const LambdaExpr *Lambda = Result.Nodes.getNodeAs("lambda"); - Diag.Report(declref->getLocStart(), errorID) - << declref->getFoundDecl() << declref->getType()->getPointeeType(); - Diag.Report(declref->getLocStart(), noteID); + for (const LambdaCapture Capture : Lambda->captures()) { + if (Capture.capturesVariable()) { + QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType(); + + if (!Pointee.isNull() && isClassRefCounted(Pointee)) { + Diag.Report(Capture.getLocation(), errorID) + << Capture.getCapturedVar() << Pointee; + Diag.Report(Capture.getLocation(), noteID); + } + } + } } void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run( diff --git a/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp index 20486bbcae87..35aac9dd6000 100644 --- a/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp +++ b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp @@ -67,9 +67,9 @@ void foo() { take(argsp); take(localsp); }); - take([ptr](R* argptr) { + take([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} R* localptr; - ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + ptr->method(); argptr->method(); localptr->method(); }); @@ -79,9 +79,9 @@ void foo() { argsp->method(); localsp->method(); }); - take([ptr](R* argptr) { + take([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} R* localptr; - take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(ptr); take(argptr); take(localptr); }); From 0d0978cd82962030e5207207d9c0eac0462afc58 Mon Sep 17 00:00:00 2001 From: Michael Layzell Date: Tue, 1 Sep 2015 10:58:37 -0400 Subject: [PATCH 012/101] Bug 1200413 - Part 2: Make lambdas in ProgressTracker.cpp capture strong references, r=seth --- image/ProgressTracker.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/image/ProgressTracker.cpp b/image/ProgressTracker.cpp index b0a76a762ece..65e8d39435dc 100644 --- a/image/ProgressTracker.cpp +++ b/image/ProgressTracker.cpp @@ -441,13 +441,14 @@ void ProgressTracker::AddObserver(IProgressObserver* aObserver) { MOZ_ASSERT(NS_IsMainThread()); + nsRefPtr observer = aObserver; mObservers.Write([=](ObserverTable* aTable) { - MOZ_ASSERT(!aTable->Get(aObserver, nullptr), + MOZ_ASSERT(!aTable->Get(observer, nullptr), "Adding duplicate entry for image observer"); - WeakPtr weakPtr = aObserver; - aTable->Put(aObserver, weakPtr); + WeakPtr weakPtr = observer.get(); + aTable->Put(observer, weakPtr); }); } @@ -455,11 +456,12 @@ bool ProgressTracker::RemoveObserver(IProgressObserver* aObserver) { MOZ_ASSERT(NS_IsMainThread()); + nsRefPtr observer = aObserver; // Remove the observer from the list. bool removed = mObservers.Write([=](ObserverTable* aTable) { - bool removed = aTable->Get(aObserver, nullptr); - aTable->Remove(aObserver); + bool removed = aTable->Get(observer, nullptr); + aTable->Remove(observer); return removed; }); From 87aa5dec6eb4d4b8ca765e90ddea0e614017673b Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Fri, 28 Aug 2015 16:39:20 -0700 Subject: [PATCH 013/101] Bug 1011786 - Diagnostic patch to detect cyclic ropes, r=terrence --HG-- extra : rebase_source : 5a30d120367d2ba71e1800c5a5b34496481b6efb --- js/src/gc/Marking.cpp | 26 ++++++++++++++++++++++++++ js/src/jsscript.cpp | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 0b9884f57e62..2f078d187e40 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -1019,7 +1019,33 @@ js::GCMarker::eagerlyMarkChildren(JSRope* rope) // types. ptrdiff_t savedPos = stack.position(); JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String); +#ifdef JS_DEBUG + static const size_t DEEP_ROPE_THRESHOLD = 100000; + static const size_t ROPE_CYCLE_HISTORY = 100; + DebugOnly ropeDepth = 0; + JSRope* history[ROPE_CYCLE_HISTORY]; +#endif while (true) { +#ifdef JS_DEBUG + if (++ropeDepth >= DEEP_ROPE_THRESHOLD) { + // Bug 1011786 comment 294 - detect cyclic ropes. There are some + // legitimate deep ropes, at least in tests. So if we hit a deep + // rope, start recording the nodes we visit and check whether we + // repeat. But do it on a finite window size W so that we're not + // scanning the full history for every node. And only check every + // Wth push, to add only constant overhead per node. This will only + // catch cycles of size up to W (but it seems most likely that any + // cycles will be size 1 or maybe 2.) + if ((ropeDepth > DEEP_ROPE_THRESHOLD + ROPE_CYCLE_HISTORY) && + (ropeDepth % ROPE_CYCLE_HISTORY) == 0) + { + for (size_t i = 0; i < ROPE_CYCLE_HISTORY; i++) + MOZ_ASSERT(history[i] != rope, "cycle detected in rope"); + } + history[ropeDepth % ROPE_CYCLE_HISTORY] = rope; + } +#endif + JS_DIAGNOSTICS_ASSERT(rope->getTraceKind() == JS::TraceKind::String); JS_DIAGNOSTICS_ASSERT(rope->JSString::isRope()); AssertZoneIsMarking(rope); diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2ccd7d872c59..02187646a6be 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3693,7 +3693,7 @@ JSScript::traceChildren(JSTracer* trc) if (hasRegexps()) { ObjectArray* objarray = regexps(); - TraceRange(trc, objarray->length, objarray->vector, "objects"); + TraceRange(trc, objarray->length, objarray->vector, "regexps"); } if (hasConsts()) { From 0f397fb78dc23cf9d59475ca695ed9eb16fcf17e Mon Sep 17 00:00:00 2001 From: "\"Kearwood \\\"Kip\\\" Gilbert\"" Date: Thu, 11 Jun 2015 13:43:40 -0700 Subject: [PATCH 014/101] Bug 1157984 - Part 1: Extend gfx::2d classes to support both float and double precision,r=jrmuizel - Implemented templates for Coord, Point, Point3D, Point4D, Size, Margin and Rect to create double precision versions. --HG-- extra : rebase_source : 91e16a7b970026346b6e90a23427fd0f70491765 --- gfx/2d/Coord.h | 39 +++++++++++------- gfx/2d/Point.h | 82 ++++++++++++++++++++------------------ gfx/2d/Rect.h | 44 ++++++++++---------- gfx/ipc/GfxMessageUtils.h | 6 +-- gfx/layers/LayersLogging.h | 2 +- gfx/thebes/gfx2DGlue.h | 10 +++++ 6 files changed, 104 insertions(+), 79 deletions(-) diff --git a/gfx/2d/Coord.h b/gfx/2d/Coord.h index 90bbcbc8a291..201f59ce50b0 100644 --- a/gfx/2d/Coord.h +++ b/gfx/2d/Coord.h @@ -7,6 +7,7 @@ #define MOZILLA_GFX_COORD_H_ #include "mozilla/Attributes.h" +#include "mozilla/TypeTraits.h" // For IsSame #include "Types.h" #include "BaseCoord.h" @@ -19,7 +20,7 @@ template struct IsPixel; namespace gfx { template struct IntCoordTyped; -template struct CoordTyped; +template struct CoordTyped; // CommonType is a metafunction that returns the type of the // result of an arithmetic operation on the underlying type of a strongly-typed @@ -37,9 +38,9 @@ struct CommonType, primitive> { typedef decltype(int32_t() + primitive()) type; }; -template -struct CommonType, primitive> { - typedef decltype(Float() + primitive()) type; +template +struct CommonType, primitive> { + typedef decltype(F() + primitive()) type; }; // This is a base class that provides mixed-type operator overloads between @@ -48,8 +49,15 @@ struct CommonType, primitive> { // convertible to their underlying value type. As we transition more of our code // to strongly-typed classes, we may be able to remove some or all of these // overloads. -template + +template struct CoordOperatorsHelper { + // Using SFINAE (Substitution Failure Is Not An Error) to suppress redundant + // operators +}; + +template +struct CoordOperatorsHelper { friend bool operator==(coord aA, primitive aB) { return aA.value == aB; } @@ -95,8 +103,8 @@ struct CoordOperatorsHelper { template struct IntCoordTyped : public BaseCoord< int32_t, IntCoordTyped >, - public CoordOperatorsHelper< IntCoordTyped, float >, - public CoordOperatorsHelper< IntCoordTyped, double > { + public CoordOperatorsHelper< true, IntCoordTyped, float >, + public CoordOperatorsHelper< true, IntCoordTyped, double > { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); @@ -106,20 +114,21 @@ struct IntCoordTyped : MOZ_CONSTEXPR MOZ_IMPLICIT IntCoordTyped(int32_t aValue) : Super(aValue) {} }; -template +template struct CoordTyped : - public BaseCoord< Float, CoordTyped >, - public CoordOperatorsHelper< CoordTyped, int32_t >, - public CoordOperatorsHelper< CoordTyped, uint32_t >, - public CoordOperatorsHelper< CoordTyped, double > { + public BaseCoord< F, CoordTyped >, + public CoordOperatorsHelper< !IsSame::value, CoordTyped, int32_t >, + public CoordOperatorsHelper< !IsSame::value, CoordTyped, uint32_t >, + public CoordOperatorsHelper< !IsSame::value, CoordTyped, double >, + public CoordOperatorsHelper< !IsSame::value, CoordTyped, float > { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef BaseCoord< Float, CoordTyped > Super; + typedef BaseCoord< F, CoordTyped > Super; MOZ_CONSTEXPR CoordTyped() : Super() {} - MOZ_CONSTEXPR MOZ_IMPLICIT CoordTyped(Float aValue) : Super(aValue) {} - explicit MOZ_CONSTEXPR CoordTyped(const IntCoordTyped& aCoord) : Super(float(aCoord.value)) {} + MOZ_CONSTEXPR MOZ_IMPLICIT CoordTyped(F aValue) : Super(aValue) {} + explicit MOZ_CONSTEXPR CoordTyped(const IntCoordTyped& aCoord) : Super(F(aCoord.value)) {} void Round() { this->value = floor(this->value + 0.5); diff --git a/gfx/2d/Point.h b/gfx/2d/Point.h index 029b7f9cc8a5..6aacdf837b31 100644 --- a/gfx/2d/Point.h +++ b/gfx/2d/Point.h @@ -64,37 +64,38 @@ struct IntPointTyped : }; typedef IntPointTyped IntPoint; -template +template struct PointTyped : - public BasePoint< Float, PointTyped, CoordTyped >, + public BasePoint< F, PointTyped, CoordTyped >, public units { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef CoordTyped Coord; - typedef BasePoint< Float, PointTyped, CoordTyped > Super; + typedef CoordTyped Coord; + typedef BasePoint< F, PointTyped, CoordTyped > Super; MOZ_CONSTEXPR PointTyped() : Super() {} - MOZ_CONSTEXPR PointTyped(Float aX, Float aY) : Super(Coord(aX), Coord(aY)) {} + MOZ_CONSTEXPR PointTyped(F aX, F aY) : Super(Coord(aX), Coord(aY)) {} // The mixed-type constructors (Float, Coord) and (Coord, Float) are needed to // avoid ambiguities because Coord is implicitly convertible to Float. - MOZ_CONSTEXPR PointTyped(Float aX, Coord aY) : Super(Coord(aX), aY) {} - MOZ_CONSTEXPR PointTyped(Coord aX, Float aY) : Super(aX, Coord(aY)) {} + MOZ_CONSTEXPR PointTyped(F aX, Coord aY) : Super(Coord(aX), aY) {} + MOZ_CONSTEXPR PointTyped(Coord aX, F aY) : Super(aX, Coord(aY)) {} MOZ_CONSTEXPR PointTyped(Coord aX, Coord aY) : Super(aX.value, aY.value) {} - MOZ_CONSTEXPR MOZ_IMPLICIT PointTyped(const IntPointTyped& point) : Super(float(point.x), float(point.y)) {} + MOZ_CONSTEXPR MOZ_IMPLICIT PointTyped(const IntPointTyped& point) : Super(F(point.x), F(point.y)) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. - static PointTyped FromUnknownPoint(const PointTyped& aPoint) { - return PointTyped(aPoint.x, aPoint.y); + static PointTyped FromUnknownPoint(const PointTyped& aPoint) { + return PointTyped(aPoint.x, aPoint.y); } - PointTyped ToUnknownPoint() const { - return PointTyped(this->x, this->y); + PointTyped ToUnknownPoint() const { + return PointTyped(this->x, this->y); } }; typedef PointTyped Point; +typedef PointTyped PointDouble; template IntPointTyped RoundedToInt(const PointTyped& aPoint) { @@ -108,57 +109,59 @@ IntPointTyped TruncatedToInt(const PointTyped& aPoint) { int32_t(aPoint.y)); } -template +template struct Point3DTyped : - public BasePoint3D< Float, Point3DTyped > { + public BasePoint3D< F, Point3DTyped > { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef BasePoint3D< Float, Point3DTyped > Super; + typedef BasePoint3D< F, Point3DTyped > Super; Point3DTyped() : Super() {} - Point3DTyped(Float aX, Float aY, Float aZ) : Super(aX, aY, aZ) {} + Point3DTyped(F aX, F aY, F aZ) : Super(aX, aY, aZ) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. - static Point3DTyped FromUnknownPoint(const Point3DTyped& aPoint) { - return Point3DTyped(aPoint.x, aPoint.y, aPoint.z); + static Point3DTyped FromUnknownPoint(const Point3DTyped& aPoint) { + return Point3DTyped(aPoint.x, aPoint.y, aPoint.z); } - Point3DTyped ToUnknownPoint() const { - return Point3DTyped(this->x, this->y, this->z); + Point3DTyped ToUnknownPoint() const { + return Point3DTyped(this->x, this->y, this->z); } }; typedef Point3DTyped Point3D; +typedef Point3DTyped PointDouble3D; -template +template struct Point4DTyped : - public BasePoint4D< Float, Point4DTyped > { + public BasePoint4D< F, Point4DTyped > { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef BasePoint4D< Float, Point4DTyped > Super; + typedef BasePoint4D< F, Point4DTyped > Super; Point4DTyped() : Super() {} - Point4DTyped(Float aX, Float aY, Float aZ, Float aW) : Super(aX, aY, aZ, aW) {} + Point4DTyped(F aX, F aY, F aZ, F aW) : Super(aX, aY, aZ, aW) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. - static Point4DTyped FromUnknownPoint(const Point4DTyped& aPoint) { - return Point4DTyped(aPoint.x, aPoint.y, aPoint.z, aPoint.w); + static Point4DTyped FromUnknownPoint(const Point4DTyped& aPoint) { + return Point4DTyped(aPoint.x, aPoint.y, aPoint.z, aPoint.w); } - Point4DTyped ToUnknownPoint() const { - return Point4DTyped(this->x, this->y, this->z, this->w); + Point4DTyped ToUnknownPoint() const { + return Point4DTyped(this->x, this->y, this->z, this->w); } - PointTyped As2DPoint() { - return PointTyped(this->x / this->w, this->y / this->w); + PointTyped As2DPoint() { + return PointTyped(this->x / this->w, this->y / this->w); } }; typedef Point4DTyped Point4D; +typedef Point4DTyped PointDouble4D; template struct IntSizeTyped : @@ -185,32 +188,33 @@ struct IntSizeTyped : }; typedef IntSizeTyped IntSize; -template +template struct SizeTyped : - public BaseSize< Float, SizeTyped >, + public BaseSize< F, SizeTyped >, public units { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef BaseSize< Float, SizeTyped > Super; + typedef BaseSize< F, SizeTyped > Super; MOZ_CONSTEXPR SizeTyped() : Super() {} - MOZ_CONSTEXPR SizeTyped(Float aWidth, Float aHeight) : Super(aWidth, aHeight) {} + MOZ_CONSTEXPR SizeTyped(F aWidth, F aHeight) : Super(aWidth, aHeight) {} explicit SizeTyped(const IntSizeTyped& size) : - Super(float(size.width), float(size.height)) {} + Super(F(size.width), F(size.height)) {} // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. - static SizeTyped FromUnknownSize(const SizeTyped& aSize) { - return SizeTyped(aSize.width, aSize.height); + static SizeTyped FromUnknownSize(const SizeTyped& aSize) { + return SizeTyped(aSize.width, aSize.height); } - SizeTyped ToUnknownSize() const { - return SizeTyped(this->width, this->height); + SizeTyped ToUnknownSize() const { + return SizeTyped(this->width, this->height); } }; typedef SizeTyped Size; +typedef SizeTyped SizeDouble; template IntSizeTyped RoundedToInt(const SizeTyped& aSize) { diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index 6bce59ce559a..d6d429d9fd98 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -35,23 +35,24 @@ struct IntMarginTyped: }; typedef IntMarginTyped IntMargin; -template +template struct MarginTyped: - public BaseMargin >, + public BaseMargin >, public units { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef BaseMargin > Super; + typedef BaseMargin > Super; MarginTyped() : Super() {} - MarginTyped(Float aTop, Float aRight, Float aBottom, Float aLeft) : + MarginTyped(F aTop, F aRight, F aBottom, F aLeft) : Super(aTop, aRight, aBottom, aLeft) {} explicit MarginTyped(const IntMarginTyped& aMargin) : - Super(float(aMargin.top), float(aMargin.right), - float(aMargin.bottom), float(aMargin.left)) {} + Super(F(aMargin.top), F(aMargin.right), + F(aMargin.bottom), F(aMargin.left)) {} }; typedef MarginTyped Margin; +typedef MarginTyped MarginDouble; template IntMarginTyped RoundedToInt(const MarginTyped& aMargin) @@ -124,23 +125,23 @@ struct IntRectTyped : }; typedef IntRectTyped IntRect; -template +template struct RectTyped : - public BaseRect, PointTyped, SizeTyped, MarginTyped >, + public BaseRect, PointTyped, SizeTyped, MarginTyped >, public units { static_assert(IsPixel::value, "'units' must be a coordinate system tag"); - typedef BaseRect, PointTyped, SizeTyped, MarginTyped > Super; + typedef BaseRect, PointTyped, SizeTyped, MarginTyped > Super; RectTyped() : Super() {} - RectTyped(const PointTyped& aPos, const SizeTyped& aSize) : + RectTyped(const PointTyped& aPos, const SizeTyped& aSize) : Super(aPos, aSize) {} - RectTyped(Float _x, Float _y, Float _width, Float _height) : + RectTyped(F _x, F _y, F _width, F _height) : Super(_x, _y, _width, _height) {} explicit RectTyped(const IntRectTyped& rect) : - Super(float(rect.x), float(rect.y), - float(rect.width), float(rect.height)) {} + Super(F(rect.x), F(rect.y), + F(rect.width), F(rect.height)) {} void NudgeToIntegers() { @@ -154,29 +155,30 @@ struct RectTyped : { *aOut = IntRectTyped(int32_t(this->X()), int32_t(this->Y()), int32_t(this->Width()), int32_t(this->Height())); - return RectTyped(Float(aOut->x), Float(aOut->y), - Float(aOut->width), Float(aOut->height)) + return RectTyped(F(aOut->x), F(aOut->y), + F(aOut->width), F(aOut->height)) .IsEqualEdges(*this); } // XXX When all of the code is ported, the following functions to convert to and from // unknown types should be removed. - static RectTyped FromUnknownRect(const RectTyped& rect) { - return RectTyped(rect.x, rect.y, rect.width, rect.height); + static RectTyped FromUnknownRect(const RectTyped& rect) { + return RectTyped(rect.x, rect.y, rect.width, rect.height); } - RectTyped ToUnknownRect() const { - return RectTyped(this->x, this->y, this->width, this->height); + RectTyped ToUnknownRect() const { + return RectTyped(this->x, this->y, this->width, this->height); } // This is here only to keep IPDL-generated code happy. DO NOT USE. - bool operator==(const RectTyped& aRect) const + bool operator==(const RectTyped& aRect) const { - return RectTyped::IsEqualEdges(aRect); + return RectTyped::IsEqualEdges(aRect); } }; typedef RectTyped Rect; +typedef RectTyped RectDouble; template IntRectTyped RoundedToInt(const RectTyped& aRect) diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 9a5a0dbaaf12..79c7e47074c3 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -527,10 +527,10 @@ struct ParamTraits< mozilla::gfx::PointTyped > } }; -template -struct ParamTraits< mozilla::gfx::Point3DTyped > +template +struct ParamTraits< mozilla::gfx::Point3DTyped > { - typedef mozilla::gfx::Point3DTyped paramType; + typedef mozilla::gfx::Point3DTyped paramType; static void Write(Message* msg, const paramType& param) { diff --git a/gfx/layers/LayersLogging.h b/gfx/layers/LayersLogging.h index 1e621e46139d..1904876384e0 100644 --- a/gfx/layers/LayersLogging.h +++ b/gfx/layers/LayersLogging.h @@ -21,7 +21,7 @@ struct gfxRGBA; namespace mozilla { namespace gfx { class Matrix4x4; -template struct RectTyped; +template struct RectTyped; } // namespace gfx enum class ImageFormat; diff --git a/gfx/thebes/gfx2DGlue.h b/gfx/thebes/gfx2DGlue.h index 7d8e68dfe653..0654aa2cb6d8 100644 --- a/gfx/thebes/gfx2DGlue.h +++ b/gfx/thebes/gfx2DGlue.h @@ -24,6 +24,11 @@ inline Rect ToRect(const gfxRect &aRect) Float(aRect.width), Float(aRect.height)); } +inline RectDouble ToRectDouble(const gfxRect &aRect) +{ + return RectDouble(aRect.x, aRect.y, aRect.width, aRect.height); +} + inline Rect ToRect(const IntRect &aRect) { return Rect(aRect.x, aRect.y, aRect.width, aRect.height); @@ -143,6 +148,11 @@ inline gfxRect ThebesRect(const Rect &aRect) return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height); } +inline gfxRect ThebesRect(const RectDouble &aRect) +{ + return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height); +} + inline gfxRGBA ThebesRGBA(const Color &aColor) { return gfxRGBA(aColor.r, aColor.g, aColor.b, aColor.a); From 5265775c940e220656af06ff13fdb40066abfc12 Mon Sep 17 00:00:00 2001 From: "Kearwood (Kip) Gilbert" Date: Thu, 6 Aug 2015 17:26:03 -0700 Subject: [PATCH 015/101] Bug 1157984 - Part 2: Implement double precision clipping functions in Matrix4x4,r=vlad - Implement Matrix4x4::TransformAndClipBounds - Update methods in Matrix4x4 with templates, allowing for both single and double precision. --HG-- extra : rebase_source : 6d5710a85bf5d82c441463debd98b31be4ec2ace --- gfx/2d/Matrix.cpp | 122 +++++++++++++++++++++++++++++++++++----------- gfx/2d/Matrix.h | 35 ++++++++----- 2 files changed, 118 insertions(+), 39 deletions(-) diff --git a/gfx/2d/Matrix.cpp b/gfx/2d/Matrix.cpp index fcfdef029d92..dc319bedcf86 100644 --- a/gfx/2d/Matrix.cpp +++ b/gfx/2d/Matrix.cpp @@ -144,12 +144,19 @@ Matrix::NudgeToIntegers() return *this; } -Rect -Matrix4x4::TransformBounds(const Rect& aRect) const +template +RectTyped +Matrix4x4::TransformBounds(const RectTyped& aRect) const { - Point quad[4]; - Float min_x, max_x; - Float min_y, max_y; + Point4DTyped verts[4]; + verts[0] = *this * Point4DTyped(aRect.x, aRect.y, 0.0, 1.0); + verts[1] = *this * Point4DTyped(aRect.XMost(), aRect.y, 0.0, 1.0); + verts[2] = *this * Point4DTyped(aRect.XMost(), aRect.YMost(), 0.0, 1.0); + verts[3] = *this * Point4DTyped(aRect.x, aRect.YMost(), 0.0, 1.0); + + PointTyped quad[4]; + F min_x, max_x; + F min_y, max_y; quad[0] = *this * aRect.TopLeft(); quad[1] = *this * aRect.TopRight(); @@ -175,7 +182,7 @@ Matrix4x4::TransformBounds(const Rect& aRect) const } } - return Rect(min_x, min_y, max_x - min_x, max_y - min_y); + return RectTyped(min_x, min_y, max_x - min_x, max_y - min_y); } Point4D ComputePerspectivePlaneIntercept(const Point4D& aFirst, @@ -272,26 +279,28 @@ Rect Matrix4x4::ProjectRectBounds(const Rect& aRect, const Rect &aClip) const return Rect(min_x, min_y, max_x - min_x, max_y - min_y); } +template size_t -Matrix4x4::TransformAndClipRect(const Rect& aRect, const Rect& aClip, - Point* aVerts) const +Matrix4x4::TransformAndClipRect(const RectTyped& aRect, + const RectTyped& aClip, + PointTyped* aVerts) const { // Initialize a double-buffered array of points in homogenous space with // the input rectangle, aRect. - Point4D points[2][kTransformAndClipRectMaxVerts]; - Point4D* dstPoint = points[0]; - *dstPoint++ = *this * Point4D(aRect.x, aRect.y, 0, 1); - *dstPoint++ = *this * Point4D(aRect.XMost(), aRect.y, 0, 1); - *dstPoint++ = *this * Point4D(aRect.XMost(), aRect.YMost(), 0, 1); - *dstPoint++ = *this * Point4D(aRect.x, aRect.YMost(), 0, 1); + Point4DTyped points[2][kTransformAndClipRectMaxVerts]; + Point4DTyped* dstPoint = points[0]; + *dstPoint++ = *this * Point4DTyped(aRect.x, aRect.y, 0, 1); + *dstPoint++ = *this * Point4DTyped(aRect.XMost(), aRect.y, 0, 1); + *dstPoint++ = *this * Point4DTyped(aRect.XMost(), aRect.YMost(), 0, 1); + *dstPoint++ = *this * Point4DTyped(aRect.x, aRect.YMost(), 0, 1); // View frustum clipping planes are described as normals originating from // the 0,0,0,0 origin. - Point4D planeNormals[4]; - planeNormals[0] = Point4D(1.0, 0.0, 0.0, -aClip.x); - planeNormals[1] = Point4D(-1.0, 0.0, 0.0, aClip.XMost()); - planeNormals[2] = Point4D(0.0, 1.0, 0.0, -aClip.y); - planeNormals[3] = Point4D(0.0, -1.0, 0.0, aClip.YMost()); + Point4DTyped planeNormals[4]; + planeNormals[0] = Point4DTyped(1.0, 0.0, 0.0, -aClip.x); + planeNormals[1] = Point4DTyped(-1.0, 0.0, 0.0, aClip.XMost()); + planeNormals[2] = Point4DTyped(0.0, 1.0, 0.0, -aClip.y); + planeNormals[3] = Point4DTyped(0.0, -1.0, 0.0, aClip.YMost()); // Iterate through each clipping plane and clip the polygon. // In each pass, we double buffer, alternating between points[0] and @@ -299,19 +308,19 @@ Matrix4x4::TransformAndClipRect(const Rect& aRect, const Rect& aClip, for (int plane=0; plane < 4; plane++) { planeNormals[plane].Normalize(); - Point4D* srcPoint = points[plane & 1]; - Point4D* srcPointEnd = dstPoint; + Point4DTyped* srcPoint = points[plane & 1]; + Point4DTyped* srcPointEnd = dstPoint; dstPoint = points[~plane & 1]; - Point4D* prevPoint = srcPointEnd - 1; - float prevDot = planeNormals[plane].DotProduct(*prevPoint); + Point4DTyped* prevPoint = srcPointEnd - 1; + F prevDot = planeNormals[plane].DotProduct(*prevPoint); while (srcPoint < srcPointEnd) { - float nextDot = planeNormals[plane].DotProduct(*srcPoint); + F nextDot = planeNormals[plane].DotProduct(*srcPoint); if ((nextDot >= 0.0) != (prevDot >= 0.0)) { // An intersection with the clipping plane has been detected. // Interpolate to find the intersecting point and emit it. - float t = -prevDot / (nextDot - prevDot); + F t = -prevDot / (nextDot - prevDot); *dstPoint++ = *srcPoint * t + *prevPoint * (1.0 - t); } @@ -328,13 +337,13 @@ Matrix4x4::TransformAndClipRect(const Rect& aRect, const Rect& aClip, size_t dstPointCount = 0; size_t srcPointCount = dstPoint - points[0]; - for (Point4D* srcPoint = points[0]; srcPoint < points[0] + srcPointCount; srcPoint++) { + for (Point4DTyped* srcPoint = points[0]; srcPoint < points[0] + srcPointCount; srcPoint++) { - Point p; + PointTyped p; if (srcPoint->w == 0.0) { // If a point lies on the intersection of the clipping planes at // (0,0,0,0), we must avoid a division by zero w component. - p = Point(0.0, 0.0); + p = PointTyped(0.0, 0.0); } else { p = srcPoint->As2DPoint(); } @@ -571,5 +580,62 @@ Matrix4x4::GetNormalVector() const return ac.CrossProduct(ab); } +template +RectTyped +Matrix4x4::TransformAndClipBounds(const RectTyped& aRect, + const RectTyped& aClip) const +{ + PointTyped verts[kTransformAndClipRectMaxVerts]; + size_t vertCount = TransformAndClipRect(aRect, aClip, verts); + + F min_x = std::numeric_limits::max(); + F min_y = std::numeric_limits::max(); + F max_x = -std::numeric_limits::max(); + F max_y = -std::numeric_limits::max(); + for (size_t i=0; i < vertCount; i++) { + min_x = std::min(min_x, verts[i].x); + max_x = std::max(max_x, verts[i].x); + min_y = std::min(min_y, verts[i].y); + max_y = std::max(max_y, verts[i].y); + } + + if (max_x < min_x || max_y < min_y) { + return RectTyped(0, 0, 0, 0); + } + + return RectTyped(min_x, min_y, max_x - min_x, max_y - min_y); + +} + +// Explicit template instantiation for float and double precision +template +size_t +Matrix4x4::TransformAndClipRect(const Rect& aRect, const Rect& aClip, + Point* aVerts) const; + +template +size_t +Matrix4x4::TransformAndClipRect(const RectDouble& aRect, + const RectDouble& aClip, + PointDouble* aVerts) const; + +template +Rect +Matrix4x4::TransformAndClipBounds(const Rect& aRect, + const Rect& aClip) const; + +template +RectDouble +Matrix4x4::TransformAndClipBounds(const RectDouble& aRect, + const RectDouble& aClip) const; + +template +Rect +Matrix4x4::TransformBounds(const Rect& aRect) const; + +template +RectDouble +Matrix4x4::TransformBounds(const RectDouble& aRect) const; + } // namespace gfx } // namespace mozilla diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h index 44f58702d6d2..741536329643 100644 --- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -508,6 +508,13 @@ public: Rect ProjectRectBounds(const Rect& aRect, const Rect &aClip) const; + /** + * TransformAndClipBounds transforms aRect as a bounding box, while clipping + * the transformed bounds to the extents of aClip. + */ + template + RectTyped TransformAndClipBounds(const RectTyped& aRect, const RectTyped& aClip) const; + /** * TransformAndClipRect projects a rectangle and clips against view frustum * clipping planes in homogenous space so that its projected vertices are @@ -518,7 +525,10 @@ public: * emit fewer that 3 vertices, indicating that aRect will not be visible * within aClip. */ - size_t TransformAndClipRect(const Rect& aRect, const Rect& aClip, Point* aVerts) const; + template + size_t TransformAndClipRect(const RectTyped& aRect, + const RectTyped& aClip, + PointTyped* aVerts) const; static const size_t kTransformAndClipRectMaxVerts = 32; static Matrix4x4 From2D(const Matrix &aMatrix) { @@ -547,9 +557,10 @@ public: return Point4D(x, y, z, w); } - Point4D operator *(const Point4D& aPoint) const + template + Point4DTyped operator *(const Point4DTyped& aPoint) const { - Point4D retPoint; + Point4DTyped retPoint; retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41; retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42; @@ -559,28 +570,30 @@ public: return retPoint; } - Point3D operator *(const Point3D& aPoint) const + template + Point3DTyped operator *(const Point3DTyped& aPoint) const { - Point4D temp(aPoint.x, aPoint.y, aPoint.z, 1); + Point4DTyped temp(aPoint.x, aPoint.y, aPoint.z, 1); temp = *this * temp; temp /= temp.w; - return Point3D(temp.x, temp.y, temp.z); + return Point3DTyped(temp.x, temp.y, temp.z); } - Point operator *(const Point &aPoint) const + template + PointTyped operator *(const PointTyped &aPoint) const { - Point4D temp(aPoint.x, aPoint.y, 0, 1); + Point4DTyped temp(aPoint.x, aPoint.y, 0, 1); temp = *this * temp; temp /= temp.w; - return Point(temp.x, temp.y); + return PointTyped(temp.x, temp.y); } - GFX2D_API Rect TransformBounds(const Rect& rect) const; - + template + GFX2D_API RectTyped TransformBounds(const RectTyped& aRect) const; static Matrix4x4 Translation(Float aX, Float aY, Float aZ) { From cbc7ba3ddb3c6dbaee8b1107535377b62209a37c Mon Sep 17 00:00:00 2001 From: "Kearwood (Kip) Gilbert" Date: Thu, 6 Aug 2015 17:26:09 -0700 Subject: [PATCH 016/101] Bug 1157984 - Part 3: Correct bounding box transformations to support projections and correct clipping when transforming behind the camera,r=vlad - Update callsites of Matrix4x4::TransformBounds to use Matrix4x4::TransformAndClipBounds. --HG-- extra : rebase_source : a1aa889af56e404b7ca5c7125021171e67a0b8bf --- gfx/2d/Rect.h | 16 +++++++++ gfx/layers/LayerTreeInvalidation.cpp | 2 +- gfx/layers/basic/BasicCompositor.cpp | 9 ++--- gfx/layers/basic/BasicLayerManager.cpp | 12 +++---- .../composite/LayerManagerComposite.cpp | 3 +- gfx/layers/composite/LayerManagerComposite.h | 2 +- gfx/layers/opengl/CompositorOGL.cpp | 2 +- gfx/src/nsRegion.cpp | 6 ++-- layout/base/nsLayoutUtils.cpp | 36 +++++++++++++------ layout/base/nsLayoutUtils.h | 1 + 10 files changed, 58 insertions(+), 31 deletions(-) diff --git a/gfx/2d/Rect.h b/gfx/2d/Rect.h index d6d429d9fd98..2cce1655f994 100644 --- a/gfx/2d/Rect.h +++ b/gfx/2d/Rect.h @@ -143,6 +143,22 @@ struct RectTyped : Super(F(rect.x), F(rect.y), F(rect.width), F(rect.height)) {} + // Returns the largest rectangle that can be represented with 32-bit + // signed integers, centered around a point at 0,0. As BaseRect's represent + // the dimensions as a top-left point with a width and height, the width + // and height will be the largest positive 32-bit value. The top-left + // position coordinate is divided by two to center the rectangle around a + // point at 0,0. + static RectTyped MaxIntRect() + { + return RectTyped( + -std::numeric_limits::max() * 0.5, + -std::numeric_limits::max() * 0.5, + std::numeric_limits::max(), + std::numeric_limits::max() + ); + }; + void NudgeToIntegers() { NudgeToInteger(&(this->x)); diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index b0d5cc6d593f..d7d59dadf9a9 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -43,7 +43,7 @@ TransformRect(const IntRect& aRect, const Matrix4x4& aTransform) } Rect rect(aRect.x, aRect.y, aRect.width, aRect.height); - rect = aTransform.TransformBounds(rect); + rect = aTransform.TransformAndClipBounds(rect, Rect::MaxIntRect()); rect.RoundOut(); IntRect intRect; diff --git a/gfx/layers/basic/BasicCompositor.cpp b/gfx/layers/basic/BasicCompositor.cpp index c327763a5754..e37c5e09a876 100644 --- a/gfx/layers/basic/BasicCompositor.cpp +++ b/gfx/layers/basic/BasicCompositor.cpp @@ -358,12 +358,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect, dest->SetTransform(Matrix::Translation(-aRect.x, -aRect.y)); // Get the bounds post-transform. - new3DTransform = aTransform; - gfxRect bounds = ThebesRect(aRect); - bounds.TransformBounds(new3DTransform); - bounds.IntersectRect(bounds, gfxRect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); - - transformBounds = ToRect(bounds); + transformBounds = aTransform.TransformAndClipBounds(aRect, Rect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); transformBounds.RoundOut(); // Propagate the coordinate offset to our 2D draw target. @@ -371,7 +366,7 @@ BasicCompositor::DrawQuad(const gfx::Rect& aRect, // When we apply the 3D transformation, we do it against a temporary // surface, so undo the coordinate offset. - new3DTransform = Matrix4x4::Translation(aRect.x, aRect.y, 0) * new3DTransform; + new3DTransform = Matrix4x4::Translation(aRect.x, aRect.y, 0) * aTransform; } newTransform.PostTranslate(-offset.x, -offset.y); diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index 6aaf2f85b4e1..0341da8bff88 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -761,14 +761,12 @@ Transform3D(RefPtr aSource, const Matrix4x4& aTransform, gfxRect& aDestRect) { - // Find the transformed rectangle of our layer. - gfxRect offsetRect = aBounds; - offsetRect.TransformBounds(aTransform); - - // Intersect the transformed layer with the destination rectangle. + // Find the transformed rectangle of our layer, intersected with the + // destination rectangle. // This is in device space since we have an identity transform set on aTarget. - aDestRect = aDest->GetClipExtents(); - aDestRect.IntersectRect(aDestRect, offsetRect); + aDestRect = ThebesRect(aTransform.TransformAndClipBounds( + ToRectDouble(aBounds), + ToRectDouble(aDest->GetClipExtents()))); aDestRect.RoundOut(); // Create a surface the size of the transformed object. diff --git a/gfx/layers/composite/LayerManagerComposite.cpp b/gfx/layers/composite/LayerManagerComposite.cpp index f218bf2969b5..34032824b0b8 100644 --- a/gfx/layers/composite/LayerManagerComposite.cpp +++ b/gfx/layers/composite/LayerManagerComposite.cpp @@ -1019,7 +1019,8 @@ SubtractTransformedRegion(nsIntRegion& aRegion, // subtract it from the screen region. nsIntRegionRectIterator it(aRegionToSubtract); while (const IntRect* rect = it.Next()) { - Rect incompleteRect = aTransform.TransformBounds(ToRect(*rect)); + Rect incompleteRect = aTransform.TransformAndClipBounds(ToRect(*rect), + Rect::MaxIntRect()); aRegion.Sub(aRegion, IntRect(incompleteRect.x, incompleteRect.y, incompleteRect.width, diff --git a/gfx/layers/composite/LayerManagerComposite.h b/gfx/layers/composite/LayerManagerComposite.h index 98c19bb200e8..ca83ff2e0f3d 100644 --- a/gfx/layers/composite/LayerManagerComposite.h +++ b/gfx/layers/composite/LayerManagerComposite.h @@ -554,7 +554,7 @@ RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor, gfx::Matrix4x4 transform = aLayer->GetEffectiveTransform(); // TODO: Use RenderTargetIntRect and TransformTo<...> here gfx::IntRect surfaceRect = - RoundedOut(transform.TransformBounds(visibleRect)).Intersect(aClipRect); + RoundedOut(transform.TransformAndClipBounds(visibleRect, gfx::Rect(aClipRect))); if (surfaceRect.IsEmpty()) { return; } diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index c74556f539cf..eb5c01d4734f 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -987,7 +987,7 @@ CompositorOGL::DrawQuad(const Rect& aRect, // XXX: This doesn't handle 3D transforms. It also doesn't handled rotated // quads. Fix me. - Rect destRect = aTransform.TransformBounds(aRect); + Rect destRect = aTransform.TransformAndClipBounds(aRect, aClipRect); mPixelsFilled += destRect.width * destRect.height; IntPoint offset = mCurrentRenderTarget->GetOrigin(); diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index 08b788319416..7006094bced0 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -612,12 +612,12 @@ TransformRect(const mozilla::gfx::IntRect& aRect, const mozilla::gfx::Matrix4x4& return mozilla::gfx::IntRect(); } - gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height); - rect.TransformBounds(aTransform); + mozilla::gfx::RectDouble rect(aRect.x, aRect.y, aRect.width, aRect.height); + rect = aTransform.TransformAndClipBounds(rect, mozilla::gfx::RectDouble::MaxIntRect()); rect.RoundOut(); mozilla::gfx::IntRect intRect; - if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) { + if (!gfxUtils::GfxRectToIntRect(ThebesRect(rect), &intRect)) { return mozilla::gfx::IntRect(); } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 7391696c4f03..5b3ec57c08c8 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -2328,22 +2328,34 @@ nsLayoutUtils::MatrixTransformRectOut(const nsRect &aBounds, { nsRect outside = aBounds; outside.ScaleRoundOut(1/aFactor); - gfxRect image = gfxRect(outside.x, outside.y, outside.width, outside.height); - image.TransformBounds(aMatrix); - return RoundGfxRectToAppRect(image, aFactor); + RectDouble image = RectDouble(outside.x, outside.y, + outside.width, outside.height); + + RectDouble maxBounds = RectDouble(double(nscoord_MIN) / aFactor * 0.5, + double(nscoord_MIN) / aFactor * 0.5, + double(nscoord_MAX) / aFactor, + double(nscoord_MAX) / aFactor); + image = aMatrix.TransformAndClipBounds(image, maxBounds); + return RoundGfxRectToAppRect(ThebesRect(image), aFactor); } nsRect nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds, const Matrix4x4 &aMatrix, float aFactor) { - gfxRect image = gfxRect(NSAppUnitsToDoublePixels(aBounds.x, aFactor), - NSAppUnitsToDoublePixels(aBounds.y, aFactor), - NSAppUnitsToDoublePixels(aBounds.width, aFactor), - NSAppUnitsToDoublePixels(aBounds.height, aFactor)); - image.TransformBounds(aMatrix); + RectDouble image = RectDouble(NSAppUnitsToDoublePixels(aBounds.x, aFactor), + NSAppUnitsToDoublePixels(aBounds.y, aFactor), + NSAppUnitsToDoublePixels(aBounds.width, aFactor), + NSAppUnitsToDoublePixels(aBounds.height, aFactor)); - return RoundGfxRectToAppRect(image, aFactor); + RectDouble maxBounds = RectDouble(double(nscoord_MIN) / aFactor * 0.5, + double(nscoord_MIN) / aFactor * 0.5, + double(nscoord_MAX) / aFactor, + double(nscoord_MAX) / aFactor); + + image = aMatrix.TransformAndClipBounds(image, maxBounds); + + return RoundGfxRectToAppRect(ThebesRect(image), aFactor); } nsPoint @@ -2687,7 +2699,11 @@ TransformGfxRectToAncestor(nsIFrame *aFrame, *aPreservesAxisAlignedRectangles = ctm.Is2D(&matrix2d) && matrix2d.PreservesAxisAlignedRectangles(); } - return ctm.TransformBounds(aRect); + Rect maxBounds = Rect(-std::numeric_limits::max() * 0.5, + -std::numeric_limits::max() * 0.5, + std::numeric_limits::max(), + std::numeric_limits::max()); + return ctm.TransformAndClipBounds(aRect, maxBounds); } static SVGTextFrame* diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index b0bd60538294..7de27debd5df 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -123,6 +123,7 @@ class nsLayoutUtils typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Rect Rect; + typedef mozilla::gfx::RectDouble RectDouble; typedef mozilla::gfx::Matrix4x4 Matrix4x4; typedef mozilla::gfx::RectCornerRadii RectCornerRadii; typedef mozilla::gfx::StrokeOptions StrokeOptions; From fb03e30b9f4840fef57897b4a562ed3e5275ec5a Mon Sep 17 00:00:00 2001 From: "Kearwood (Kip) Gilbert" Date: Thu, 6 Aug 2015 17:28:38 -0700 Subject: [PATCH 017/101] Bug 1157984 - Part 4: Remove gfxRect::TransformBounds,r=vlad --HG-- extra : rebase_source : 1ef637037fc4084f8d6b19dc25175501697b96e0 --- gfx/thebes/gfxRect.cpp | 30 ------------------------------ gfx/thebes/gfxRect.h | 5 ----- 2 files changed, 35 deletions(-) diff --git a/gfx/thebes/gfxRect.cpp b/gfx/thebes/gfxRect.cpp index a3b39fc6f2aa..a418f394e913 100644 --- a/gfx/thebes/gfxRect.cpp +++ b/gfx/thebes/gfxRect.cpp @@ -30,36 +30,6 @@ gfxRect::TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const return gfxQuad(points[0], points[1], points[2], points[3]); } -void -gfxRect::TransformBounds(const mozilla::gfx::Matrix4x4 &aMatrix) -{ - gfxPoint quad[4]; - - quad[0] = TopLeft(); - quad[1] = TopRight(); - quad[2] = BottomLeft(); - quad[3] = BottomRight(); - - quad[0].Transform(aMatrix); - double min_x = quad[0].x; - double max_x = quad[0].x; - double min_y = quad[0].y; - double max_y = quad[0].y; - - for (int i=1; i<4; i++) { - quad[i].Transform(aMatrix); - min_x = std::min(quad[i].x, min_x); - max_x = std::max(quad[i].x, max_x); - min_y = std::min(quad[i].y, min_y); - max_y = std::max(quad[i].y, max_y); - } - - x = min_x; - y = min_y; - width = max_x - min_x; - height = max_y - min_y; -} - static bool WithinEpsilonOfInteger(gfxFloat aX, gfxFloat aEpsilon) { diff --git a/gfx/thebes/gfxRect.h b/gfx/thebes/gfxRect.h index 78f0998768e2..d5045c51ec4c 100644 --- a/gfx/thebes/gfxRect.h +++ b/gfx/thebes/gfxRect.h @@ -150,11 +150,6 @@ struct gfxRect : * Transform this rectangle with aMatrix, resulting in a gfxQuad. */ gfxQuad TransformToQuad(const mozilla::gfx::Matrix4x4 &aMatrix) const; - - /* - * Transform this rectangle with aMatrix, as an axis-aligned bounding box - */ - void TransformBounds(const mozilla::gfx::Matrix4x4 &aMatrix); }; #endif /* GFX_RECT_H */ From 0d5944c188f672f21a51b9898bd8c7a5435e61c2 Mon Sep 17 00:00:00 2001 From: "Kearwood (Kip) Gilbert" Date: Wed, 26 Aug 2015 16:39:52 -0700 Subject: [PATCH 018/101] Bug 1157984 - Part 5: Test,r=vlad - Implemented a reftest to verify that the transformed element is clipped against the w=0 plane without disappearing --HG-- extra : rebase_source : 26ad8c58b95c0af384a14b2c70fffe80190b8a17 --- layout/reftests/transform-3d/1157984-1.html | 32 +++++++++++++++++++++ layout/reftests/transform-3d/reftest.list | 1 + 2 files changed, 33 insertions(+) create mode 100644 layout/reftests/transform-3d/1157984-1.html diff --git a/layout/reftests/transform-3d/1157984-1.html b/layout/reftests/transform-3d/1157984-1.html new file mode 100644 index 000000000000..be0f6526aa43 --- /dev/null +++ b/layout/reftests/transform-3d/1157984-1.html @@ -0,0 +1,32 @@ + + + + Testcase, bug 1157984 + + + + +
+
+
+ + diff --git a/layout/reftests/transform-3d/reftest.list b/layout/reftests/transform-3d/reftest.list index 3703ff67c6d2..80cfa1159059 100644 --- a/layout/reftests/transform-3d/reftest.list +++ b/layout/reftests/transform-3d/reftest.list @@ -57,6 +57,7 @@ pref(layout.css.will-change.enabled,true) != willchange-containing-block.html?wi fuzzy-if(winWidget&&!layersGPUAccelerated,1,606) == scroll-perspective-1.html scroll-perspective-1-ref.html # Bugs fails-if(!layersGPUAccelerated) == 1035611-1.html 1035611-1-ref.html # Bug 1072898 for !layersGPUAccelerated failures +!= 1157984-1.html about:blank # Bug 1157984 fuzzy(3,99) == animate-cube-radians.html animate-cube-radians-ref.html # subpixel AA fuzzy(3,99) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated,16,6) fuzzy-if(Mulet,16,9) == animate-cube-radians-zoom.html animate-cube-radians-zoom-ref.html != animate-cube-radians-ref.html animate-cube-radians-zoom-ref.html From 0a90c052e6b35f00577d74e6093f1b2a4b80e345 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 31 Aug 2015 22:57:24 -0400 Subject: [PATCH 019/101] Bug 1200492 - Do not add the top source directory to the include path in dom/canvas; r=gps Otherwise, putting a file named a system header there will get it included. --- dom/canvas/moz.build | 1 - 1 file changed, 1 deletion(-) diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index 461847306cf8..39288fe6be89 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -139,7 +139,6 @@ UNIFIED_SOURCES += [ ] LOCAL_INCLUDES += [ - '../..', # Support `#include "mfbt/RefPtr.h"` '/js/xpconnect/wrappers', ] From 1dface560b1bc21d4e6a85982f03b389b1ca88d5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 14 Jul 2015 20:35:37 -0700 Subject: [PATCH 020/101] Bug 1044077 - Tweak some jemalloc constants. r=glandium. --HG-- extra : rebase_source : bfdb94191aa0872fd1aff52f143c63b0a3e41848 --- memory/mozjemalloc/jemalloc.c | 22 +++++++++++----------- memory/mozjemalloc/jemalloc_types.h | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/memory/mozjemalloc/jemalloc.c b/memory/mozjemalloc/jemalloc.c index f746347f3c39..7e64ff5d72b8 100644 --- a/memory/mozjemalloc/jemalloc.c +++ b/memory/mozjemalloc/jemalloc.c @@ -4227,7 +4227,7 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero) if (zero == false) { #ifdef MALLOC_FILL if (opt_junk) - memset(ret, 0xa5, size); + memset(ret, 0xe4, size); else if (opt_zero) memset(ret, 0, size); #endif @@ -4263,7 +4263,7 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero) if (zero == false) { #ifdef MALLOC_FILL if (opt_junk) - memset(ret, 0xa5, size); + memset(ret, 0xe4, size); else if (opt_zero) memset(ret, 0, size); #endif @@ -4365,7 +4365,7 @@ arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size) #ifdef MALLOC_FILL if (opt_junk) - memset(ret, 0xa5, size); + memset(ret, 0xe4, size); else if (opt_zero) memset(ret, 0, size); #endif @@ -4586,7 +4586,7 @@ arena_dalloc_small(arena_t *arena, arena_chunk_t *chunk, void *ptr, #ifdef MALLOC_FILL if (opt_poison) - memset(ptr, 0x5a, size); + memset(ptr, 0xe5, size); #endif arena_run_reg_dalloc(run, bin, ptr, size); @@ -4679,7 +4679,7 @@ arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr) #ifdef MALLOC_STATS if (opt_poison) #endif - memset(ptr, 0x5a, size); + memset(ptr, 0xe5, size); #endif #ifdef MALLOC_STATS arena->stats.allocated_large -= size; @@ -4818,7 +4818,7 @@ arena_ralloc_large(void *ptr, size_t size, size_t oldsize) /* Same size class. */ #ifdef MALLOC_FILL if (opt_poison && size < oldsize) { - memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - + memset((void *)((uintptr_t)ptr + size), 0xe5, oldsize - size); } #endif @@ -4835,7 +4835,7 @@ arena_ralloc_large(void *ptr, size_t size, size_t oldsize) #ifdef MALLOC_FILL /* Fill before shrinking in order avoid a race. */ if (opt_poison) { - memset((void *)((uintptr_t)ptr + size), 0x5a, + memset((void *)((uintptr_t)ptr + size), 0xe5, oldsize - size); } #endif @@ -4905,7 +4905,7 @@ arena_ralloc(void *ptr, size_t size, size_t oldsize) IN_PLACE: #ifdef MALLOC_FILL if (opt_poison && size < oldsize) - memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size); + memset((void *)((uintptr_t)ptr + size), 0xe5, oldsize - size); else if (opt_zero && size > oldsize) memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize); #endif @@ -5125,9 +5125,9 @@ huge_palloc(size_t size, size_t alignment, bool zero) if (zero == false) { if (opt_junk) # ifdef MALLOC_DECOMMIT - memset(ret, 0xa5, psize); + memset(ret, 0xe4, psize); # else - memset(ret, 0xa5, csize); + memset(ret, 0xe4, csize); # endif else if (opt_zero) # ifdef MALLOC_DECOMMIT @@ -5154,7 +5154,7 @@ huge_ralloc(void *ptr, size_t size, size_t oldsize) size_t psize = PAGE_CEILING(size); #ifdef MALLOC_FILL if (opt_poison && size < oldsize) { - memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize + memset((void *)((uintptr_t)ptr + size), 0xe5, oldsize - size); } #endif diff --git a/memory/mozjemalloc/jemalloc_types.h b/memory/mozjemalloc/jemalloc_types.h index 57afd03594ca..ae8dc4414dfb 100644 --- a/memory/mozjemalloc/jemalloc_types.h +++ b/memory/mozjemalloc/jemalloc_types.h @@ -55,8 +55,8 @@ typedef struct { * Run-time configuration settings. */ jemalloc_bool opt_abort; /* abort(3) on error? */ - jemalloc_bool opt_junk; /* Fill allocated memory with 0xa5/0x5a? */ - jemalloc_bool opt_poison; /* Fill free memory with 0xa5/0x5a? */ + jemalloc_bool opt_junk; /* Fill allocated memory with 0xe4? */ + jemalloc_bool opt_poison; /* Fill free memory with 0xe5? */ jemalloc_bool opt_utrace; /* Trace all allocation events? */ jemalloc_bool opt_sysv; /* SysV semantics? */ jemalloc_bool opt_xmalloc; /* abort(3) on OOM? */ From 7c77cbc5d385d6ac7a5b6acd1f23b2ac13e32a04 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Tue, 1 Sep 2015 17:52:46 -0700 Subject: [PATCH 021/101] Bug 1195445 - Update host key fingerprint for bugzilla.mozilla.org The certificate was updated to a SHA-2 certificate. DONTBUILD (NPOTB) --HG-- extra : commitid : 11QMFkjpCTa extra : rebase_source : b9e005c278ef223066a58d78029ccbb4a1c1c9e4 extra : amend_source : bb1bcd8003fa644ce4c8876ec2aa7ac20790ad9e --- tools/mercurial/hgsetup/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mercurial/hgsetup/config.py b/tools/mercurial/hgsetup/config.py index eed57363d71e..ff3152b49a77 100644 --- a/tools/mercurial/hgsetup/config.py +++ b/tools/mercurial/hgsetup/config.py @@ -12,7 +12,7 @@ import os HOST_FINGERPRINTS = { 'bitbucket.org': '46:de:34:e7:9b:18:cd:7f:ae:fd:8b:e3:bc:f4:1a:5e:38:d7:ac:24', - 'bugzilla.mozilla.org': '47:13:a2:14:0c:46:45:53:12:0d:e5:36:16:a5:60:26:3e:da:3a:60', + 'bugzilla.mozilla.org': 'f9:7e:62:42:4e:38:79:96:ca:87:71:2a:f8:51:38:c8:16:92:5c:a7', 'hg.mozilla.org': 'af:27:b9:34:47:4e:e5:98:01:f6:83:2b:51:c9:aa:d8:df:fb:1a:27', } From 411d08fdb3f9893fd417afe33a6acafd65178743 Mon Sep 17 00:00:00 2001 From: chunminchang Date: Thu, 20 Aug 2015 23:38:00 -0400 Subject: [PATCH 022/101] Bug 1114507 - Part 1: Add/release the appId's refcnt if frame is in main process. r=kanru --- dom/base/nsFrameLoader.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 7265c4df0276..7a507ab13ec5 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -157,7 +157,6 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated) , mObservingOwnerContent(false) , mVisible(true) { - ResetPermissionManagerStatus(); mRemoteFrame = ShouldUseRemoteProcess(); } @@ -409,6 +408,9 @@ nsFrameLoader::ReallyStartLoadingInternal() mURIToLoad = nullptr; NS_ENSURE_SUCCESS(rv, rv); + // Track the appId's reference count if this frame is in-process + ResetPermissionManagerStatus(); + return NS_OK; } @@ -2705,7 +2707,9 @@ nsFrameLoader::ResetPermissionManagerStatus() { // The resetting of the permissions status can run only // in the main process. - if (XRE_IsContentProcess()) { + // only in-main-process && in-process frame is handled here and all other + // cases are handled by ContentParent. + if (XRE_IsContentProcess() || mRemoteFrame) { return; } From f85525b7d46f6865fed894ac1fe32a7cd47bdc81 Mon Sep 17 00:00:00 2001 From: chunminchang Date: Fri, 28 Aug 2015 03:18:00 -0400 Subject: [PATCH 023/101] Bug 1114507 - Part 2: Add/release the appId's refcnt in oop case. r=kanru --- dom/ipc/ContentParent.cpp | 137 +++++++++++++++++++++++++----- dom/ipc/ContentParent.h | 28 +++++- dom/ipc/ContentProcessManager.cpp | 36 ++++++++ dom/ipc/ContentProcessManager.h | 16 ++++ dom/ipc/PContent.ipdl | 9 +- dom/ipc/TabParent.cpp | 54 +++++++++--- dom/ipc/TabParent.h | 1 + 7 files changed, 244 insertions(+), 37 deletions(-) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 38a3416eb737..0fe27ab5bca3 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1468,7 +1468,9 @@ ContentParent::Init() } Preferences::AddStrongObserver(this, ""); if (obs) { - obs->NotifyObservers(static_cast(this), "ipc:content-created", nullptr); + nsAutoString cpId; + cpId.AppendInt(static_cast(this->ChildID())); + obs->NotifyObservers(static_cast(this), "ipc:content-created", cpId.get()); } #ifdef ACCESSIBILITY @@ -2057,7 +2059,9 @@ ContentParent::ActorDestroy(ActorDestroyReason why) } #endif } - obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nullptr); + nsAutoString cpId; + cpId.AppendInt(static_cast(this->ChildID())); + obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get()); } // Remove any and all idle listeners. @@ -2085,10 +2089,35 @@ ContentParent::ActorDestroy(ActorDestroyReason why) // least until after the current task finishes running. NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this)); - // Destroy any processes created by this ContentParent - ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + // Release the appId's reference count of any processes + // created by this ContentParent and the frame opened by this ContentParent + // if this ContentParent crashes. + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); nsTArray childIDArray = cpm->GetAllChildProcessById(this->ChildID()); + if (why == AbnormalShutdown) { + nsCOMPtr permMgr = services::GetPermissionManager(); + if(permMgr) { + // Release the appId's reference count of its child-processes + for (uint32_t i = 0; i < childIDArray.Length(); i++) { + nsTArray tabCtxs = cpm->GetTabContextByContentProcess(childIDArray[i]); + for (uint32_t j = 0 ; j < tabCtxs.Length() ; j++) { + if (tabCtxs[j].OwnOrContainingAppId() != nsIScriptSecurityManager::NO_APP_ID) { + permMgr->ReleaseAppId(tabCtxs[j].OwnOrContainingAppId()); + } + } + } + // Release the appId's reference count belong to itself + nsTArray tabCtxs = cpm->GetTabContextByContentProcess(mChildID); + for (uint32_t i = 0; i < tabCtxs.Length() ; i++) { + if (tabCtxs[i].OwnOrContainingAppId()!= nsIScriptSecurityManager::NO_APP_ID) { + permMgr->ReleaseAppId(tabCtxs[i].OwnOrContainingAppId()); + } + } + } + } + + // Destroy any processes created by this ContentParent for(uint32_t i = 0; i < childIDArray.Length(); i++) { ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]); MessageLoop::current()->PostTask( @@ -2104,23 +2133,33 @@ ContentParent::ActorDestroy(ActorDestroyReason why) } void -ContentParent::NotifyTabDestroying(PBrowserParent* aTab) +ContentParent::NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) { + if (XRE_IsParentProcess()) { // There can be more than one PBrowser for a given app process // because of popup windows. PBrowsers can also destroy // concurrently. When all the PBrowsers are destroying, kick off // another task to ensure the child process *really* shuts down, // even if the PBrowsers themselves never finish destroying. - int32_t numLiveTabs = ManagedPBrowserParent().Length(); - ++mNumDestroyingTabs; - if (mNumDestroyingTabs != numLiveTabs) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + if (!cp) { + return; + } + ++cp->mNumDestroyingTabs; + nsTArray tabIds = cpm->GetTabParentsByProcessId(aCpId); + if (static_cast(cp->mNumDestroyingTabs) != tabIds.Length()) { return; } // We're dying now, so prevent this content process from being // recycled during its shutdown procedure. - MarkAsDead(); - StartForceKillTimer(); + cp->MarkAsDead(); + cp->StartForceKillTimer(); + } else { + ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId); + } } void @@ -2142,16 +2181,15 @@ ContentParent::StartForceKillTimer() } void -ContentParent::NotifyTabDestroyed(PBrowserParent* aTab, +ContentParent::NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying) { if (aNotifiedDestroying) { --mNumDestroyingTabs; } - TabId id = static_cast(aTab)->GetTabId(); nsTArray parentArray = - nsContentPermissionUtils::GetContentPermissionRequestParentById(id); + nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId); // Need to close undeleted ContentPermissionRequestParents before tab is closed. for (auto& permissionRequestParent : parentArray) { @@ -2164,7 +2202,9 @@ ContentParent::NotifyTabDestroyed(PBrowserParent* aTab, // There can be more than one PBrowser for a given app process // because of popup windows. When the last one closes, shut // us down. - if (ManagedPBrowserParent().Length() == 1) { + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + nsTArray tabIds = cpm->GetTabParentsByProcessId(this->ChildID()); + if (tabIds.Length() == 1) { // In the case of normal shutdown, send a shutdown message to child to // allow it to perform shutdown tasks. MessageLoop::current()->PostTask( @@ -4899,8 +4939,12 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId, { TabId tabId; if (XRE_IsParentProcess()) { - ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId); + // Add appId's reference count in oop case + if (tabId) { + PermissionManagerAddref(aCpId, tabId); + } } else { ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId, @@ -4913,14 +4957,27 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId, /*static*/ void ContentParent::DeallocateTabId(const TabId& aTabId, - const ContentParentId& aCpId) + const ContentParentId& aCpId, + bool aMarkedDestroying) { if (XRE_IsParentProcess()) { + // Release appId's reference count in oop case + if (aTabId) { + PermissionManagerRelease(aCpId, aTabId); + } + + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + ContentParent* cp = cpm->GetContentProcessById(aCpId); + + cp->NotifyTabDestroyed(aTabId, aMarkedDestroying); + ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId); } else { - ContentChild::GetSingleton()->SendDeallocateTabId(aTabId); + ContentChild::GetSingleton()->SendDeallocateTabId(aTabId, + aCpId, + aMarkedDestroying); } } @@ -4938,9 +4995,19 @@ ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId, } bool -ContentParent::RecvDeallocateTabId(const TabId& aTabId) +ContentParent::RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) { - DeallocateTabId(aTabId, this->ChildID()); + DeallocateTabId(aTabId, aCpId, aMarkedDestroying); + return true; +} + +bool +ContentParent::RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) +{ + NotifyTabDestroying(aTabId, aCpId); return true; } @@ -5111,6 +5178,38 @@ ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestP return true; } +/* static */ bool +ContentParent::PermissionManagerAddref(const ContentParentId& aCpId, + const TabId& aTabId) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Call PermissionManagerAddref in content process!"); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId); + nsCOMPtr permMgr = services::GetPermissionManager(); + if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) { + permMgr->AddrefAppId(appId); + return true; + } + return false; +} + +/* static */ bool +ContentParent::PermissionManagerRelease(const ContentParentId& aCpId, + const TabId& aTabId) +{ + MOZ_ASSERT(XRE_IsParentProcess(), + "Call PermissionManagerRelease in content process!"); + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + uint32_t appId = cpm->GetAppIdByProcessAndTabId(aCpId, aTabId); + nsCOMPtr permMgr = services::GetPermissionManager(); + if (appId != nsIScriptSecurityManager::NO_APP_ID && permMgr) { + permMgr->ReleaseAppId(appId); + return true; + } + return false; +} + bool ContentParent::RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index ff0525fec03d..4021b0608ab4 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -212,9 +212,10 @@ public: virtual bool KillChild() override; /** Notify that a tab is beginning its destruction sequence. */ - void NotifyTabDestroying(PBrowserParent* aTab); + static void NotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId); /** Notify that a tab was destroyed during normal operation. */ - void NotifyTabDestroyed(PBrowserParent* aTab, + void NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying); TestShellParent* CreateTestShell(); @@ -227,7 +228,21 @@ public: const IPCTabContext& aContext, const ContentParentId& aCpId); static void - DeallocateTabId(const TabId& aTabId, const ContentParentId& aCpId); + DeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + bool aMarkedDestroying); + + /* + * Add the appId's reference count by the given ContentParentId and TabId + */ + static bool + PermissionManagerAddref(const ContentParentId& aCpId, const TabId& aTabId); + + /* + * Release the appId's reference count by the given ContentParentId and TabId + */ + static bool + PermissionManagerRelease(const ContentParentId& aCpId, const TabId& aTabId); static bool GetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration& aConfig); @@ -356,7 +371,12 @@ public: const ContentParentId& aCpId, TabId* aTabId) override; - virtual bool RecvDeallocateTabId(const TabId& aTabId) override; + virtual bool RecvDeallocateTabId(const TabId& aTabId, + const ContentParentId& aCpId, + const bool& aMarkedDestroying) override; + + virtual bool RecvNotifyTabDestroying(const TabId& aTabId, + const ContentParentId& aCpId) override; nsTArray GetManagedTabContext(); diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp index 51c3588c0855..8937c777fdd6 100644 --- a/dom/ipc/ContentProcessManager.cpp +++ b/dom/ipc/ContentProcessManager.cpp @@ -12,6 +12,7 @@ #include "mozilla/ClearOnShutdown.h" #include "nsPrintfCString.h" +#include "nsIScriptSecurityManager.h" // XXX need another bug to move this to a common header. #ifdef DISABLE_ASSERTS_FOR_FUZZING @@ -334,5 +335,40 @@ ContentProcessManager::GetTopLevelTabParentByProcessAndTabId(const ContentParent return GetTabParentByProcessAndTabId(currentCpId, currentTabId); } +nsTArray +ContentProcessManager::GetTabParentsByProcessId(const ContentParentId& aChildCpId) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsTArray tabIdList; + auto iter = mContentParentMap.find(aChildCpId); + if (NS_WARN_IF(iter == mContentParentMap.end())) { + ASSERT_UNLESS_FUZZING(); + return Move(tabIdList); + } + + for (auto remoteFrameIter = iter->second.mRemoteFrames.begin(); + remoteFrameIter != iter->second.mRemoteFrames.end(); + ++remoteFrameIter) { + tabIdList.AppendElement(remoteFrameIter->first); + } + + return Move(tabIdList); +} + +uint32_t +ContentProcessManager::GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId) +{ + uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; + if (aChildCpId && aChildTabId) { + TabContext tabContext; + if (GetTabContextByProcessAndTabId(aChildCpId, aChildTabId, &tabContext)) { + appId = tabContext.OwnOrContainingAppId(); + } + } + return appId; +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentProcessManager.h b/dom/ipc/ContentProcessManager.h index ea5b36e96e9b..4821af50f979 100644 --- a/dom/ipc/ContentProcessManager.h +++ b/dom/ipc/ContentProcessManager.h @@ -110,6 +110,13 @@ public: const TabId& aChildTabId, /*out*/ TabId* aOpenerTabId); + /** + * Get all TabParents' Ids managed by the givent content process. + * Return empty array when TabParent couldn't be found via aChildCpId + */ + nsTArray + GetTabParentsByProcessId(const ContentParentId& aChildCpId); + /** * Get the TabParent by the given content process and tab id. * Return nullptr when TabParent couldn't be found via aChildCpId @@ -135,6 +142,15 @@ public: GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId, const TabId& aChildTabId); + /** + * Return appId by given TabId and ContentParentId. + * It will return nsIScriptSecurityManager::NO_APP_ID + * if the given tab is not an app. + */ + uint32_t + GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, + const TabId& aChildTabId); + private: static StaticAutoPtr sSingleton; TabId mUniqueId; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 080385f4341c..451090bc76b1 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -991,8 +991,15 @@ parent: */ sync AllocateTabId(TabId openerTabId, IPCTabContext context, ContentParentId cpId) returns (TabId tabId); - async DeallocateTabId(TabId tabId); + async DeallocateTabId(TabId tabId, + ContentParentId cpId, + bool aMarkedDestroying); + /** + * Tell the chrome process there is a destruction of PBrowser(Tab) + */ + async NotifyTabDestroying(TabId tabId, + ContentParentId cpId); /** * Starts an offline application cache update. * @param manifestURI diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index c4c82df953da..233c71b846d7 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -436,12 +436,8 @@ TabParent::IsVisible() } void -TabParent::Destroy() +TabParent::DestroyInternal() { - if (mIsDestroyed) { - return; - } - IMEStateManager::OnTabParentDestroying(this); RemoveWindowListeners(); @@ -455,11 +451,6 @@ TabParent::Destroy() RemoveTabParentFromTable(frame->GetLayersId()); frame->Destroy(); } - mIsDestroyed = true; - - if (XRE_IsParentProcess()) { - Manager()->AsContentParent()->NotifyTabDestroying(this); - } // Let all PluginWidgets know we are tearing down. Prevents // these objects from sending async events after the child side @@ -468,6 +459,24 @@ TabParent::Destroy() for (uint32_t idx = 0; idx < kids.Length(); ++idx) { static_cast(kids[idx])->ParentDestroy(); } +} + +void +TabParent::Destroy() +{ + if (mIsDestroyed) { + return; + } + + DestroyInternal(); + + mIsDestroyed = true; + + if (XRE_IsParentProcess()) { + ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->AsContentParent()->ChildID()); + } else { + ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID()); + } mMarkedDestroying = true; } @@ -476,12 +485,15 @@ bool TabParent::Recv__delete__() { if (XRE_IsParentProcess()) { - Manager()->AsContentParent()->NotifyTabDestroyed(this, mMarkedDestroying); ContentParent::DeallocateTabId(mTabId, - Manager()->AsContentParent()->ChildID()); + Manager()->AsContentParent()->ChildID(), + mMarkedDestroying); } else { - ContentParent::DeallocateTabId(mTabId, ContentParentId(0)); + Manager()->AsContentBridgeParent()->NotifyTabDestroyed(); + ContentParent::DeallocateTabId(mTabId, + Manager()->ChildID(), + mMarkedDestroying); } return true; @@ -494,6 +506,22 @@ TabParent::ActorDestroy(ActorDestroyReason why) // case of a crash. IMEStateManager::OnTabParentDestroying(this); + // Prevent executing ContentParent::NotifyTabDestroying in + // TabParent::Destroy() called by frameLoader->DestroyComplete() below + // when tab crashes in contentprocess because ContentParent::ActorDestroy() + // in main process will be triggered before this function + // and remove the process information that + // ContentParent::NotifyTabDestroying need from mContentParentMap. + + // When tab crashes in content process, + // there is no need to call ContentParent::NotifyTabDestroying + // because the jobs in ContentParent::NotifyTabDestroying + // will be done by ContentParent::ActorDestroy. + if (XRE_IsContentProcess() && why == AbnormalShutdown && !mIsDestroyed) { + DestroyInternal(); + mIsDestroyed = true; + } + nsRefPtr frameLoader = GetFrameLoader(true); nsCOMPtr os = services::GetObserverService(); if (frameLoader) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index c8b61d1cbefa..67d2b7dab8ec 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -491,6 +491,7 @@ protected: LayoutDeviceIntPoint mChromeOffset; private: + void DestroyInternal(); already_AddRefed GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const; nsRefPtr mManager; void TryCacheDPIAndScale(); From 3ccf001aaa4f43799e00976ade26df17702b5230 Mon Sep 17 00:00:00 2001 From: chunminchang Date: Mon, 31 Aug 2015 01:53:00 -0400 Subject: [PATCH 024/101] Bug 1114507 - Part 3: Remove PContetBridge channel when grandchild-process is killed. r=kanru --- dom/ipc/ContentBridgeChild.cpp | 24 +----------------------- dom/ipc/ContentBridgeChild.h | 3 --- dom/ipc/ContentBridgeParent.cpp | 11 +++++++++++ dom/ipc/ContentBridgeParent.h | 2 ++ dom/ipc/nsIContentParent.cpp | 8 ++++++++ dom/ipc/nsIContentParent.h | 3 +++ 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/dom/ipc/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp index 0c8dce8614f3..fc72ea5097be 100644 --- a/dom/ipc/ContentBridgeChild.cpp +++ b/dom/ipc/ContentBridgeChild.cpp @@ -12,7 +12,6 @@ #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "nsIObserverService.h" using namespace mozilla::ipc; using namespace mozilla::jsipc; @@ -21,8 +20,7 @@ namespace mozilla { namespace dom { NS_IMPL_ISUPPORTS(ContentBridgeChild, - nsIContentChild, - nsIObserver) + nsIContentChild) ContentBridgeChild::ContentBridgeChild(Transport* aTransport) : mTransport(aTransport) @@ -36,10 +34,6 @@ ContentBridgeChild::~ContentBridgeChild() void ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { - nsCOMPtr os = mozilla::services::GetObserverService(); - if (os) { - os->RemoveObserver(this, "content-child-shutdown"); - } MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy)); @@ -55,11 +49,6 @@ ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid) DebugOnly ok = bridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop()); MOZ_ASSERT(ok); - nsCOMPtr os = mozilla::services::GetObserverService(); - if (os) { - os->AddObserver(bridge, "content-child-shutdown", false); - } - return bridge; } @@ -180,16 +169,5 @@ ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor) return nsIContentChild::DeallocPBlobChild(aActor); } -NS_IMETHODIMP -ContentBridgeChild::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - if (!strcmp(aTopic, "content-child-shutdown")) { - Close(); - } - return NS_OK; -} - } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentBridgeChild.h b/dom/ipc/ContentBridgeChild.h index b04c2f137cfb..44a198193919 100644 --- a/dom/ipc/ContentBridgeChild.h +++ b/dom/ipc/ContentBridgeChild.h @@ -9,20 +9,17 @@ #include "mozilla/dom/PContentBridgeChild.h" #include "mozilla/dom/nsIContentChild.h" -#include "nsIObserver.h" namespace mozilla { namespace dom { class ContentBridgeChild final : public PContentBridgeChild , public nsIContentChild - , public nsIObserver { public: explicit ContentBridgeChild(Transport* aTransport); NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER static ContentBridgeChild* Create(Transport* aTransport, ProcessId aOtherProcess); diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp index c21a8e4c0651..b269928b60ce 100644 --- a/dom/ipc/ContentBridgeParent.cpp +++ b/dom/ipc/ContentBridgeParent.cpp @@ -163,6 +163,17 @@ ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent) return nsIContentParent::DeallocPBrowserParent(aParent); } +void +ContentBridgeParent::NotifyTabDestroyed() +{ + int32_t numLiveTabs = ManagedPBrowserParent().Length(); + if (numLiveTabs == 1) { + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod(this, &ContentBridgeParent::Close)); + } +} + // This implementation is identical to ContentParent::GetCPOWManager but we can't // move it to nsIContentParent because it calls ManagedPJavaScriptParent() which // only exists in PContentParent and PContentBridgeParent. diff --git a/dom/ipc/ContentBridgeParent.h b/dom/ipc/ContentBridgeParent.h index af2fbc1acd1e..3b2c0e3807c2 100644 --- a/dom/ipc/ContentBridgeParent.h +++ b/dom/ipc/ContentBridgeParent.h @@ -28,6 +28,8 @@ public: virtual void ActorDestroy(ActorDestroyReason aWhy) override; void DeferredDestroy(); + virtual bool IsContentBridgeParent() override { return true; } + void NotifyTabDestroyed(); static ContentBridgeParent* Create(Transport* aTransport, ProcessId aOtherProcess); diff --git a/dom/ipc/nsIContentParent.cpp b/dom/ipc/nsIContentParent.cpp index 1ec21a1fb410..71b4cdc747a8 100644 --- a/dom/ipc/nsIContentParent.cpp +++ b/dom/ipc/nsIContentParent.cpp @@ -10,6 +10,7 @@ #include "mozilla/Preferences.h" #include "mozilla/dom/File.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentBridgeParent.h" #include "mozilla/dom/PTabContext.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/StructuredCloneUtils.h" @@ -46,6 +47,13 @@ nsIContentParent::AsContentParent() return static_cast(this); } +ContentBridgeParent* +nsIContentParent::AsContentBridgeParent() +{ + MOZ_ASSERT(IsContentBridgeParent()); + return static_cast(this); +} + PJavaScriptParent* nsIContentParent::AllocPJavaScriptParent() { diff --git a/dom/ipc/nsIContentParent.h b/dom/ipc/nsIContentParent.h index 758f8efdf425..29a50f25d172 100644 --- a/dom/ipc/nsIContentParent.h +++ b/dom/ipc/nsIContentParent.h @@ -38,6 +38,7 @@ class BlobConstructorParams; class BlobImpl; class BlobParent; class ContentParent; +class ContentBridgeParent; class IPCTabContext; class PBlobParent; class PBrowserParent; @@ -77,6 +78,8 @@ public: virtual bool IsContentParent() { return false; } ContentParent* AsContentParent(); + virtual bool IsContentBridgeParent() { return false; } + ContentBridgeParent* AsContentBridgeParent(); protected: // methods bool CanOpenBrowser(const IPCTabContext& aContext); From 6efd887296ffae82eafc2eca2d3cdda94d42682f Mon Sep 17 00:00:00 2001 From: chunminchang Date: Thu, 20 Aug 2015 23:42:00 -0400 Subject: [PATCH 025/101] Bug 1114507 - Part 4: Test cases. r=kanru --- dom/ipc/tests/file_app.sjs | 55 +++ dom/ipc/tests/file_app.template.webapp | 6 + dom/ipc/tests/mochitest.ini | 36 ++ dom/ipc/tests/test_permission_embed.html | 51 +++ .../test_permission_for_in_process_app.html | 43 +++ .../test_permission_for_nested_oop_app.html | 75 ++++ .../tests/test_permission_for_oop_app.html | 45 +++ .../test_permission_for_two_oop_apps.html | 88 +++++ dom/ipc/tests/test_permission_framescript.js | 31 ++ dom/ipc/tests/test_permission_helper.js | 362 ++++++++++++++++++ .../test_permission_when_oop_app_crashes.html | 67 ++++ 11 files changed, 859 insertions(+) create mode 100644 dom/ipc/tests/file_app.sjs create mode 100644 dom/ipc/tests/file_app.template.webapp create mode 100644 dom/ipc/tests/test_permission_embed.html create mode 100644 dom/ipc/tests/test_permission_for_in_process_app.html create mode 100644 dom/ipc/tests/test_permission_for_nested_oop_app.html create mode 100644 dom/ipc/tests/test_permission_for_oop_app.html create mode 100644 dom/ipc/tests/test_permission_for_two_oop_apps.html create mode 100644 dom/ipc/tests/test_permission_framescript.js create mode 100644 dom/ipc/tests/test_permission_helper.js create mode 100644 dom/ipc/tests/test_permission_when_oop_app_crashes.html diff --git a/dom/ipc/tests/file_app.sjs b/dom/ipc/tests/file_app.sjs new file mode 100644 index 000000000000..45c002d99ebc --- /dev/null +++ b/dom/ipc/tests/file_app.sjs @@ -0,0 +1,55 @@ +var gBasePath = "tests/dom/ipc/tests/"; + +function handleRequest(request, response) { + var query = getQuery(request); + + var testToken = ''; + if ('testToken' in query) { + testToken = query.testToken; + } + + var template = ''; + if ('template' in query) { + template = query.template; + } + var template = gBasePath + template; + response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); + response.write(readTemplate(template).replace(/TESTTOKEN/g, testToken)); +} + +// Copy-pasted incantations. There ought to be a better way to synchronously read +// a file into a string, but I guess we're trying to discourage that. +function readTemplate(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + var split = path.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + cis.init(fis, "UTF-8", 0, 0); + + var data = ""; + let str = {}; + let read = 0; + do { + read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value + data += str.value; + } while (read != 0); + cis.close(); + return data; +} + +function getQuery(request) { + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + return query; +} diff --git a/dom/ipc/tests/file_app.template.webapp b/dom/ipc/tests/file_app.template.webapp new file mode 100644 index 000000000000..8e7ae0750bd4 --- /dev/null +++ b/dom/ipc/tests/file_app.template.webapp @@ -0,0 +1,6 @@ +{ + "name": "Nested-OOP", + "description": "Nested-OOP test app used for mochitest.", + "launch_path": "/tests/dom/ipc/tests/TESTTOKEN", + "icons": { "128": "default_icon" } +} diff --git a/dom/ipc/tests/mochitest.ini b/dom/ipc/tests/mochitest.ini index ee25c1b09ae7..b2aec1bc0378 100644 --- a/dom/ipc/tests/mochitest.ini +++ b/dom/ipc/tests/mochitest.ini @@ -21,3 +21,39 @@ skip-if = true # bug 1166923 skip-if = toolkit == 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf [test_CrashService_crash.html] skip-if = !(crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows') && (buildapp != 'b2g' || toolkit == 'gonk') && (buildapp != 'mulet')) # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables. +[test_permission_for_in_process_app.html] +skip-if = e10s || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + test_permission_helper.js +[test_permission_for_oop_app.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js +[test_permission_for_nested_oop_app.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js +[test_permission_for_two_oop_apps.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js +[test_permission_when_oop_app_crashes.html] +skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app +support-files = + file_app.sjs + file_app.template.webapp + test_permission_helper.js + test_permission_embed.html + test_permission_framescript.js diff --git a/dom/ipc/tests/test_permission_embed.html b/dom/ipc/tests/test_permission_embed.html new file mode 100644 index 000000000000..88c0051d6f1c --- /dev/null +++ b/dom/ipc/tests/test_permission_embed.html @@ -0,0 +1,51 @@ + + + + + oop-test apps + + + + + + diff --git a/dom/ipc/tests/test_permission_for_in_process_app.html b/dom/ipc/tests/test_permission_for_in_process_app.html new file mode 100644 index 000000000000..7112efc6114b --- /dev/null +++ b/dom/ipc/tests/test_permission_for_in_process_app.html @@ -0,0 +1,43 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_for_nested_oop_app.html b/dom/ipc/tests/test_permission_for_nested_oop_app.html new file mode 100644 index 000000000000..461ddd16ee41 --- /dev/null +++ b/dom/ipc/tests/test_permission_for_nested_oop_app.html @@ -0,0 +1,75 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_for_oop_app.html b/dom/ipc/tests/test_permission_for_oop_app.html new file mode 100644 index 000000000000..781715aa5907 --- /dev/null +++ b/dom/ipc/tests/test_permission_for_oop_app.html @@ -0,0 +1,45 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_for_two_oop_apps.html b/dom/ipc/tests/test_permission_for_two_oop_apps.html new file mode 100644 index 000000000000..9e2182515f28 --- /dev/null +++ b/dom/ipc/tests/test_permission_for_two_oop_apps.html @@ -0,0 +1,88 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + diff --git a/dom/ipc/tests/test_permission_framescript.js b/dom/ipc/tests/test_permission_framescript.js new file mode 100644 index 000000000000..d2bd63b84644 --- /dev/null +++ b/dom/ipc/tests/test_permission_framescript.js @@ -0,0 +1,31 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); +let cm = Services.crashmanager; +var cpIdList = []; + +var shutdownObs = { + observe: function(subject, topic, data) { + if (topic == 'ipc:content-shutdown') { + var index = cpIdList.indexOf(parseInt(data)); + if (index > -1) { + cpIdList.splice(index, 1); + sendAsyncMessage('content-shutdown', ''); + } + } + } +}; + +var createdObs = { + observe: function(subject, topic, data) { + if (topic == 'ipc:content-created') { + cpIdList.push(parseInt(data)); + sendAsyncMessage('content-created', ''); + } + } +}; + +addMessageListener('crashreporter-status', function(message) { + sendAsyncMessage('crashreporter-enable', !!cm); +}); + +Services.obs.addObserver(shutdownObs, 'ipc:content-shutdown', false); +Services.obs.addObserver(createdObs, 'ipc:content-created', false); diff --git a/dom/ipc/tests/test_permission_helper.js b/dom/ipc/tests/test_permission_helper.js new file mode 100644 index 000000000000..125da9139035 --- /dev/null +++ b/dom/ipc/tests/test_permission_helper.js @@ -0,0 +1,362 @@ +"use strict"; + +// Used to access the DOM node in this test +const DOM_PARENT_ID = "container"; +const DOM_PARENT = document.getElementById(DOM_PARENT_ID); +const APP_IFRAME_ID = "appFrame"; +const APP_IFRAME_ID2 = "appFrame2"; + +// Settings for testing the permission +const SESSION_PERSIST_MINUTES = 10; +const PERMISSION_TYPE = "geolocation"; +const permManager = SpecialPowers.Cc["@mozilla.org/permissionmanager;1"] + .getService(SpecialPowers.Ci.nsIPermissionManager); + +// Used to access App's information like appId +const gAppsService = SpecialPowers.Cc["@mozilla.org/AppsService;1"] + .getService(SpecialPowers.Ci.nsIAppsService); + +// Target-app for this testing +const APP_URL = "http://example.org"; +const APP_MANIFEST = "http://example.org/manifest.webapp"; + +// Used to embed a remote app that open the test-target-app above +const embedAppHostedManifestURL = window.location.origin + + '/tests/dom/ipc/tests/file_app.sjs?testToken=test_permission_embed.html&template=file_app.template.webapp'; +var embedApp; + +// Used to add listener for ipc:content-shutdown that +// will be triggered after ContentParent::DeallocateTabId +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('test_permission_framescript.js')); + +// Delay reporting a result until after finish() got called +SimpleTest.waitForExplicitFinish(); +// Allow tests to disable the per platform app validity checks +SpecialPowers.setAllAppsLaunchable(true); + +// Run tests in order +function runTests() { + if (!tests.length) { + ok(true, "pass all tests!"); + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + +function test1() { + allocateAppFrame(APP_IFRAME_ID, DOM_PARENT, APP_URL, APP_MANIFEST); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission( PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 1] App should have permission: ' + PERMISSION_TYPE); + } + + removeAppFrame(APP_IFRAME_ID); + + // We expect there is no permission for the test-target-app + // after removing the in-process iframe embedding this app + runNextIfAppHasPermission(1, false, APP_URL, APP_MANIFEST); +} + +function test2() { + var afterShutdown = function () { + // We expect there is no permission for the test-target-app + // after removing the remote iframe embedding this app + runNextIfAppHasPermission(2, false, APP_URL, APP_MANIFEST); + }; + + // Test permission after the child-process containing + // test-target-app is killed + afterContentShutdown(1, afterShutdown); + + allocateAppFrame(APP_IFRAME_ID, DOM_PARENT, APP_URL, APP_MANIFEST, true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission( PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 2] App should have permission: ' + PERMISSION_TYPE); + } + + removeAppFrame(APP_IFRAME_ID); +} + +function test3() { + var afterGrandchildShutdown = function () { + // We expect there is no permission for the test-target-app + // after removing the remote iframe embedding this app + runNextIfAppHasPermission(3, false, APP_URL, APP_MANIFEST); + }; + + // Test permission after the grandchild-process + // containing test-target-app is killed + afterContentShutdown(1, afterGrandchildShutdown); + + allocateAppFrame(APP_IFRAME_ID, + DOM_PARENT, + embedApp.manifest.launch_path, + embedApp.manifestURL, + true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission(PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 3] App should have permission: ' + PERMISSION_TYPE); + } +} + +function test4() { + var afterGrandchildShutdown = function () { + // We expect there is still a permission for the test-target-app + // after killing test-target-app on 3rd-level process + runNextIfAppHasPermission(4, true, APP_URL, APP_MANIFEST); + }; + + // Test permission after the grandchild-process + // containing test-target-app is killed + afterContentShutdown(1, afterGrandchildShutdown); + + // Open a child process(2nd level) and a grandchild process(3rd level) + // that contains a test-target-app + allocateAppFrame(APP_IFRAME_ID, + DOM_PARENT, + embedApp.manifest.launch_path, + embedApp.manifestURL, + true); + + // Open another child process that contains + // another test-target-app(2nd level) + allocateAppFrame(APP_IFRAME_ID2, + DOM_PARENT, + APP_URL, + APP_MANIFEST, + true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission(PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 4] App should have permission: ' + PERMISSION_TYPE); + } +} + +function test5() { + var afterShutdown = function () { + // We expect there is no permission for the test-target-app + // after crashing its parent-process + runNextIfAppHasPermission(5, false, APP_URL, APP_MANIFEST); + }; + + // Test permission after the parent-process of test-target-app is crashed. + afterContentShutdown(2, afterShutdown); + + allocateAppFrame(APP_IFRAME_ID, + DOM_PARENT, + embedApp.manifest.launch_path + '#' + encodeURIComponent('crash'), + embedApp.manifestURL, + true); + + var appId = gAppsService.getAppLocalIdByManifestURL(APP_MANIFEST); + + if (!SpecialPowers.hasPermission( PERMISSION_TYPE, + { url: APP_URL, + appId: appId, + isInBrowserElement: false })) { + errorHandler('[test 5] App should have permission: ' + PERMISSION_TYPE); + } + + // Crash the child-process on 2nd level after + // the grandchild process on 3rd is allocated + var handler = {'crash': function() { + gScript.sendAsyncMessage("crashreporter-status", {}); + + getCrashReporterStatus(function(enable) { + if (enable) { + SimpleTest.expectChildProcessCrash(); + } + crashChildProcess(APP_IFRAME_ID); + }); + } + }; + + receiveMessageFromChildFrame(APP_IFRAME_ID, handler); +} + +// Setup the prefrences and permissions. +// This function should be called before any tests +function setupPrefsAndPermissions() { + SpecialPowers.pushPrefEnv({"set": [ + ["dom.mozBrowserFramesEnabled", true], + ["dom.ipc.tabs.nested.enabled", true], + ["dom.ipc.tabs.disabled", false]]}, + function() { + var autoManageApp = function () { + SpecialPowers.setAllAppsLaunchable(true); + // No confirmation needed when an app is installed and uninstalled. + SpecialPowers.autoConfirmAppInstall(() => { + // This callback should trigger the first test + SpecialPowers.autoConfirmAppUninstall(runTests); + }); + }; + + setupOpenAppPermission(document, autoManageApp); + } + ); +} + +function setupOpenAppPermission(ctx, callback) { + SpecialPowers.pushPermissions([ + { "type": "browser", "allow": true, "context": ctx}, // for mozbrowser + { "type": "embed-apps", "allow": true, "context": ctx }, // for mozapp + { "type": "webapps-manage", "allow": true, "context": ctx }], // for (un)installing apps + function checkPermissions() { + if (SpecialPowers.hasPermission("browser", ctx) && + SpecialPowers.hasPermission("embed-apps", ctx) && + SpecialPowers.hasPermission("webapps-manage", ctx)) { + callback(); + } else { + errorHandler(">> At least one of required permissions to open app is disallowed!\n"); + } + }); +} + +function installApp() { + // Install App from manifest + var request = navigator.mozApps.install(embedAppHostedManifestURL); + request.onerror = cbError; + request.onsuccess = function() { + // Get installed app + embedApp = request.result; // Assign to global variable + runTests(); + } +} + +function uninstallApp() { + var request = navigator.mozApps.mgmt.uninstall(embedApp); + request.onerror = cbError; + request.onsuccess = function() { + info("uninstall app susseccfully!"); + runTests(); + } +} + +function removeAppFrame(id) { + var ifr = document.getElementById(id); + ifr.remove(); +} + +function allocateAppFrame(id, parentNode, appSRC, manifestURL, useRemote = false) { + var ifr = document.createElement('iframe'); + ifr.setAttribute('id', id); + ifr.setAttribute('remote', useRemote? "true" : "false"); + ifr.setAttribute('mozbrowser', 'true'); + ifr.setAttribute('mozapp', manifestURL); + ifr.setAttribute('src', appSRC); + parentNode.appendChild(ifr); +} + +function receiveMessageFromChildFrame(id, handlers) { + var ifr = document.getElementById(id); + ifr.addEventListener('mozbrowsershowmodalprompt', function (recvMsg) { + var msg = recvMsg.detail.message; + handlers[msg](); + }); +} + +function addPermissionToApp(appURL, manifestURL) { + var appId = gAppsService.getAppLocalIdByManifestURL(manifestURL); + + // Get the time now. This is used for permission manager's expire_time + var now = Number(Date.now()); + + // Add app's permission asynchronously + SpecialPowers.pushPermissions([ + { "type":PERMISSION_TYPE, + "allow": 1, + "expireType":permManager.EXPIRE_SESSION, + "expireTime":now + SESSION_PERSIST_MINUTES*60*1000, + "context": { url: appURL, + appId: appId, + isInBrowserElement:false } + } + ], function() { + runTests(); + }); +} + +function runNextIfAppHasPermission(round, expect, appURL, manifestURL) { + var appId = gAppsService.getAppLocalIdByManifestURL(manifestURL); + + var hasPerm = SpecialPowers.hasPermission(PERMISSION_TYPE, + { url: appURL, + appId: appId, + isInBrowserElement: false }); + var result = (expect==hasPerm); + if (result) { + runTests(); + } else { + errorHandler( '[test ' + round + '] App should ' + ((expect)? '':'NOT ') + + 'have permission: ' + PERMISSION_TYPE); + } +} + +function afterContentShutdown(times, callback) { + // handle the message from test_permission_framescript.js + var num = 0; + gScript.addMessageListener('content-shutdown', function onShutdown(data) { + num += 1; + if (num >= times) { + gScript.removeMessageListener('content-shutdown', onShutdown); + callback(); + } + }); +} + +function getCrashReporterStatus(callback) { + gScript.addMessageListener('crashreporter-enable', function getStatus(data) { + gScript.removeMessageListener('crashreporter-enable', getStatus); + callback(data); + }); +} + +// Inject a frame script that crashes the content process +function crashChildProcess(frameId) { + var child = document.getElementById(frameId); + var mm = SpecialPowers.getBrowserFrameMessageManager(child); + var childFrameScriptStr = + 'function ContentScriptScope() {' + + 'var Cu = Components.utils;' + + 'Cu.import("resource://gre/modules/ctypes.jsm");' + + 'var crash = function() {' + + 'var zero = new ctypes.intptr_t(8);' + + 'var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));' + + 'badptr.contents;' + + '};' + + 'privateNoteIntentionalCrash();' + + 'crash();' + + '}'; + mm.loadFrameScript('data:,new ' + childFrameScriptStr, false); +} + +function errorHandler(errMsg) { + ok(false, errMsg); + SimpleTest.finish(); +} + +function cbError(e) { + errorHandler("Error callback invoked: " + this.error.name); +} diff --git a/dom/ipc/tests/test_permission_when_oop_app_crashes.html b/dom/ipc/tests/test_permission_when_oop_app_crashes.html new file mode 100644 index 000000000000..f1f5dd52bb4f --- /dev/null +++ b/dom/ipc/tests/test_permission_when_oop_app_crashes.html @@ -0,0 +1,67 @@ + + + + + + Test for Bug 1114507 + + + + +Mozilla Bug1114507 +

+ + + + + From 62a02d2e47b95d43b0cb29ffcb84f1fff2e4587b Mon Sep 17 00:00:00 2001 From: Vincent Liu Date: Wed, 26 Aug 2015 18:58:18 +0800 Subject: [PATCH 026/101] Bug 1198574 - Remove unnecessary argument for PersistentBufferProvider. r=bas --- gfx/layers/Layers.cpp | 4 ++-- gfx/layers/PersistentBufferProvider.cpp | 4 ++-- gfx/layers/PersistentBufferProvider.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 1ca4cd452a07..07bb51989d43 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -165,12 +165,12 @@ LayerManager::CreatePersistentBufferProvider(const mozilla::gfx::IntSize &aSize, mozilla::gfx::SurfaceFormat aFormat) { RefPtr bufferProvider = - new PersistentBufferProviderBasic(this, aSize, aFormat, + new PersistentBufferProviderBasic(aSize, aFormat, gfxPlatform::GetPlatform()->GetPreferredCanvasBackend()); if (!bufferProvider->IsValid()) { bufferProvider = - new PersistentBufferProviderBasic(this, aSize, aFormat, + new PersistentBufferProviderBasic(aSize, aFormat, gfxPlatform::GetPlatform()->GetFallbackCanvasBackend()); } diff --git a/gfx/layers/PersistentBufferProvider.cpp b/gfx/layers/PersistentBufferProvider.cpp index 368921bf07ac..ec3e0600c247 100644 --- a/gfx/layers/PersistentBufferProvider.cpp +++ b/gfx/layers/PersistentBufferProvider.cpp @@ -16,8 +16,8 @@ using namespace gfx; namespace layers { -PersistentBufferProviderBasic::PersistentBufferProviderBasic(LayerManager* aManager, gfx::IntSize aSize, - gfx::SurfaceFormat aFormat, gfx::BackendType aBackend) +PersistentBufferProviderBasic::PersistentBufferProviderBasic(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + gfx::BackendType aBackend) { mDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(aBackend, aSize, aFormat); } diff --git a/gfx/layers/PersistentBufferProvider.h b/gfx/layers/PersistentBufferProvider.h index 97f0e4257dac..35835b7e9459 100644 --- a/gfx/layers/PersistentBufferProvider.h +++ b/gfx/layers/PersistentBufferProvider.h @@ -57,8 +57,8 @@ class PersistentBufferProviderBasic : public PersistentBufferProvider public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PersistentBufferProviderBasic) - PersistentBufferProviderBasic(LayerManager* aManager, gfx::IntSize aSize, - gfx::SurfaceFormat aFormat, gfx::BackendType aBackend); + PersistentBufferProviderBasic(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, + gfx::BackendType aBackend); explicit PersistentBufferProviderBasic(gfx::DrawTarget* aTarget) : mDrawTarget(aTarget) {} bool IsValid() { return !!mDrawTarget; } From 866bb24e7eec9f8505518ef693bac73722d8e0c4 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Fri, 28 Aug 2015 03:47:00 -0400 Subject: [PATCH 027/101] Bug 1149923 - Let 2D mask effect can check whether to use IntermediateSurface or not in its own logic. r=roc --- gfx/2d/Matrix.h | 9 ++++++++- gfx/layers/Layers.cpp | 31 ++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h index 741536329643..c0decefd51f9 100644 --- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -379,11 +379,18 @@ public: /** * Returns true if the matrix has any transform other * than a translation or scale; this is, if there is - * no rotation. + * rotation. */ bool HasNonAxisAlignedTransform() const { return !FuzzyEqual(_21, 0.0) || !FuzzyEqual(_12, 0.0); } + + /** + * Returns true if the matrix has negative scaling (i.e. flip). + */ + bool HasNegativeScaling() const { + return (_11 < 0.0) || (_22 < 0.0); + } }; class Matrix4x4 diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 07bb51989d43..27f691c084f7 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -1224,12 +1224,30 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToS } else { useIntermediateSurface = false; gfx::Matrix contTransform; - if (!mEffectiveTransform.Is2D(&contTransform) || + bool checkClipRect = false; + bool checkMaskLayers = false; + + if (!mEffectiveTransform.Is2D(&contTransform)) { + // In 3D case, always check if we should use IntermediateSurface. + checkClipRect = true; + checkMaskLayers = true; + } else { #ifdef MOZ_GFX_OPTIMIZE_MOBILE - !contTransform.PreservesAxisAlignedRectangles()) { + if (!contTransform.PreservesAxisAlignedRectangles()) { #else - gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) { + if (gfx::ThebesMatrix(contTransform).HasNonIntegerTranslation()) { #endif + checkClipRect = true; + } + /* In 2D case, only translation and/or positive scaling can be done w/o using IntermediateSurface. + * Otherwise, when rotation or flip happen, we should check whether to use IntermediateSurface. + */ + if (contTransform.HasNonAxisAlignedTransform() || contTransform.HasNegativeScaling()) { + checkMaskLayers = true; + } + } + + if (checkClipRect || checkMaskLayers) { for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) { const Maybe& clipRect = child->GetEffectiveClipRect(); /* We can't (easily) forward our transform to children with a non-empty clip @@ -1237,8 +1255,11 @@ ContainerLayer::DefaultComputeEffectiveTransforms(const Matrix4x4& aTransformToS * the calculations performed by CalculateScissorRect above. * Nor for a child with a mask layer. */ - if ((clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty()) || - child->HasMaskLayers()) { + if (checkClipRect && (clipRect && !clipRect->IsEmpty() && !child->GetVisibleRegion().IsEmpty())) { + useIntermediateSurface = true; + break; + } + if (checkMaskLayers && child->HasMaskLayers()) { useIntermediateSurface = true; break; } From 68a1353ed5318ba50495e485288f2bf5d1e777ac Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Mon, 31 Aug 2015 20:35:00 -0400 Subject: [PATCH 028/101] Bug 1149923 - Add reftest to testify whether mask layer is rendered correctly. r=roc --- gfx/tests/reftest/1149923-ref.html | 28 ++++++++++++++++++++++++++++ gfx/tests/reftest/1149923.html | 29 +++++++++++++++++++++++++++++ gfx/tests/reftest/reftest.list | 1 + 3 files changed, 58 insertions(+) create mode 100644 gfx/tests/reftest/1149923-ref.html create mode 100644 gfx/tests/reftest/1149923.html diff --git a/gfx/tests/reftest/1149923-ref.html b/gfx/tests/reftest/1149923-ref.html new file mode 100644 index 000000000000..46625a786da2 --- /dev/null +++ b/gfx/tests/reftest/1149923-ref.html @@ -0,0 +1,28 @@ + + + + + + + Testcase for bug 1149923 + + + +
+ + diff --git a/gfx/tests/reftest/1149923.html b/gfx/tests/reftest/1149923.html new file mode 100644 index 000000000000..ec5f777ad340 --- /dev/null +++ b/gfx/tests/reftest/1149923.html @@ -0,0 +1,29 @@ + + + + + + + Testcase for bug 1149923 + + + +
+ + diff --git a/gfx/tests/reftest/reftest.list b/gfx/tests/reftest/reftest.list index 836f23f156ab..9ef61564b23b 100644 --- a/gfx/tests/reftest/reftest.list +++ b/gfx/tests/reftest/reftest.list @@ -5,3 +5,4 @@ skip-if(B2G) fuzzy-if(Android&&AndroidVersion>=15,8,1000) == 709477-1.html 70947 skip-if(!asyncPan) == 1086723.html 1086723-ref.html == 853889-1.html 853889-1-ref.html == 1143303-1.svg pass.svg +fuzzy(30,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius \ No newline at end of file From ff26d1d901aabc5ea0c5d35d38f47a4dbff13725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Ja=C5=A1=C3=AD=C4=8Dek?= Date: Tue, 1 Sep 2015 04:09:00 -0400 Subject: [PATCH 029/101] Bug 1198256 - Replace deprecated GtkColorSelectionDialog with GtkColorChooserDialog in Gtk3. r=karlt --- widget/gtk/mozgtk/mozgtk.c | 6 ++ widget/gtk/nsColorPicker.cpp | 122 +++++++++++++++++++++++++++-------- widget/gtk/nsColorPicker.h | 27 ++++++-- 3 files changed, 123 insertions(+), 32 deletions(-) diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c index dfa6667c8412..79ebcb075df5 100644 --- a/widget/gtk/mozgtk/mozgtk.c +++ b/widget/gtk/mozgtk/mozgtk.c @@ -562,6 +562,12 @@ STUB(gtk_app_chooser_get_type) STUB(gtk_app_chooser_get_app_info) STUB(gtk_app_chooser_dialog_get_type) STUB(gtk_app_chooser_dialog_set_heading) +STUB(gtk_color_chooser_dialog_new) +STUB(gtk_color_chooser_dialog_get_type) +STUB(gtk_color_chooser_get_type) +STUB(gtk_color_chooser_set_rgba) +STUB(gtk_color_chooser_get_rgba) +STUB(gtk_color_chooser_set_use_alpha) #endif #ifdef GTK2_SYMBOLS diff --git a/widget/gtk/nsColorPicker.cpp b/widget/gtk/nsColorPicker.cpp index 06a21e3cc34f..639bc4b501d7 100644 --- a/widget/gtk/nsColorPicker.cpp +++ b/widget/gtk/nsColorPicker.cpp @@ -13,6 +13,25 @@ NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker) +#if GTK_CHECK_VERSION(3,4,0) +int nsColorPicker::convertGdkRgbaComponent(gdouble color_component) { + // GdkRGBA value is in range [0.0..1.0]. We need something in range [0..255] + return color_component * 255 + 0.5; +} + +gdouble nsColorPicker::convertToGdkRgbaComponent(int color_component) { + return color_component / 255.0; +} + +GdkRGBA nsColorPicker::convertToRgbaColor(nscolor color) { + GdkRGBA result = { convertToGdkRgbaComponent(NS_GET_R(color)), + convertToGdkRgbaComponent(NS_GET_G(color)), + convertToGdkRgbaComponent(NS_GET_B(color)), + convertToGdkRgbaComponent(NS_GET_A(color)) }; + + return result; +} +#else int nsColorPicker::convertGdkColorComponent(guint16 color_component) { // GdkColor value is in range [0..65535]. We need something in range [0..255] return (color_component * 255 + 127) / 65535; @@ -36,6 +55,7 @@ GtkColorSelection* nsColorPicker::WidgetGetColorSelection(GtkWidget* widget) return GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection( GTK_COLOR_SELECTION_DIALOG(widget))); } +#endif NS_IMETHODIMP nsColorPicker::Init(nsIDOMWindow *parent, const nsAString& title, @@ -63,8 +83,6 @@ NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback *aColorPickerShown return NS_ERROR_FAILURE; } - GdkColor color_gdk = convertToGdkColor(color); - if (mCallback) { // It means Open has already been called: this is not allowed NS_WARNING("mCallback is already set. Open called twice?"); @@ -74,21 +92,41 @@ NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback *aColorPickerShown nsXPIDLCString title; title.Adopt(ToNewUTF8String(mTitle)); - GtkWidget *color_chooser = gtk_color_selection_dialog_new(title); - GtkWindow *parent_window = GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)); + +#if GTK_CHECK_VERSION(3,4,0) + GtkWidget* color_chooser = gtk_color_chooser_dialog_new(title, parent_window); + + if (parent_window) { + gtk_window_set_destroy_with_parent(GTK_WINDOW(color_chooser), TRUE); + } + + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE); + GdkRGBA color_rgba = convertToRgbaColor(color); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_chooser), + &color_rgba); + + g_signal_connect(GTK_COLOR_CHOOSER(color_chooser), "color-activated", + G_CALLBACK(OnColorChanged), this); +#else + GtkWidget *color_chooser = gtk_color_selection_dialog_new(title); + if (parent_window) { GtkWindow *window = GTK_WINDOW(color_chooser); gtk_window_set_transient_for(window, parent_window); gtk_window_set_destroy_with_parent(window, TRUE); } + GdkColor color_gdk = convertToGdkColor(color); gtk_color_selection_set_current_color(WidgetGetColorSelection(color_chooser), &color_gdk); - - NS_ADDREF_THIS(); + g_signal_connect(WidgetGetColorSelection(color_chooser), "color-changed", G_CALLBACK(OnColorChanged), this); +#endif + + NS_ADDREF_THIS(); + g_signal_connect(color_chooser, "response", G_CALLBACK(OnResponse), this); g_signal_connect(color_chooser, "destroy", G_CALLBACK(OnDestroy), this); gtk_widget_show(color_chooser); @@ -96,6 +134,31 @@ NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback *aColorPickerShown return NS_OK; } +#if GTK_CHECK_VERSION(3,4,0) +/* static */ void +nsColorPicker::OnColorChanged(GtkColorChooser* color_chooser, GdkRGBA* color, + gpointer user_data) +{ + static_cast(user_data)->Update(color); +} + +void +nsColorPicker::Update(GdkRGBA* color) +{ + SetColor(color); + if (mCallback) { + mCallback->Update(mColor); + } +} + +void nsColorPicker::SetColor(const GdkRGBA* color) +{ + mColor.Assign('#'); + mColor += ToHexString(convertGdkRgbaComponent(color->red)); + mColor += ToHexString(convertGdkRgbaComponent(color->green)); + mColor += ToHexString(convertGdkRgbaComponent(color->blue)); +} +#else /* static */ void nsColorPicker::OnColorChanged(GtkColorSelection* colorselection, gpointer user_data) @@ -103,6 +166,27 @@ nsColorPicker::OnColorChanged(GtkColorSelection* colorselection, static_cast(user_data)->Update(colorselection); } +void +nsColorPicker::Update(GtkColorSelection* colorselection) +{ + ReadValueFromColorSelection(colorselection); + if (mCallback) { + mCallback->Update(mColor); + } +} + +void nsColorPicker::ReadValueFromColorSelection(GtkColorSelection* colorselection) +{ + GdkColor rgba; + gtk_color_selection_get_current_color(colorselection, &rgba); + + mColor.Assign('#'); + mColor += ToHexString(convertGdkColorComponent(rgba.red)); + mColor += ToHexString(convertGdkColorComponent(rgba.green)); + mColor += ToHexString(convertGdkColorComponent(rgba.blue)); +} +#endif + /* static */ void nsColorPicker::OnResponse(GtkWidget* color_chooser, gint response_id, gpointer user_data) @@ -118,22 +202,19 @@ nsColorPicker::OnDestroy(GtkWidget* color_chooser, gpointer user_data) Done(color_chooser, GTK_RESPONSE_CANCEL); } -void -nsColorPicker::Update(GtkColorSelection* colorselection) -{ - ReadValueFromColorSelection(colorselection); - if (mCallback) { - mCallback->Update(mColor); - } -} - void nsColorPicker::Done(GtkWidget* color_chooser, gint response) { switch (response) { case GTK_RESPONSE_OK: case GTK_RESPONSE_ACCEPT: +#if GTK_CHECK_VERSION(3,4,0) + GdkRGBA color; + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(color_chooser), &color); + SetColor(&color); +#else ReadValueFromColorSelection(WidgetGetColorSelection(color_chooser)); +#endif break; case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_CLOSE: @@ -167,14 +248,3 @@ nsString nsColorPicker::ToHexString(int n) result.AppendInt(n, 16); return result; } - -void nsColorPicker::ReadValueFromColorSelection(GtkColorSelection* colorselection) -{ - GdkColor rgba; - gtk_color_selection_get_current_color(colorselection, &rgba); - - mColor.Assign('#'); - mColor += ToHexString(convertGdkColorComponent(rgba.red)); - mColor += ToHexString(convertGdkColorComponent(rgba.green)); - mColor += ToHexString(convertGdkColorComponent(rgba.blue)); -} diff --git a/widget/gtk/nsColorPicker.h b/widget/gtk/nsColorPicker.h index 2c947246da65..3783dd958606 100644 --- a/widget/gtk/nsColorPicker.h +++ b/widget/gtk/nsColorPicker.h @@ -25,23 +25,38 @@ public: private: ~nsColorPicker() {}; - static void OnColorChanged(GtkColorSelection* colorselection, - gpointer user_data); + static nsString ToHexString(int n); + static void OnResponse(GtkWidget* dialog, gint response_id, gpointer user_data); static void OnDestroy(GtkWidget* dialog, gpointer user_data); + +#if GTK_CHECK_VERSION(3,4,0) + static void OnColorChanged(GtkColorChooser* color_chooser, GdkRGBA* color, + gpointer user_data); + + static int convertGdkRgbaComponent(gdouble color_component); + static gdouble convertToGdkRgbaComponent(int color_component); + static GdkRGBA convertToRgbaColor(nscolor color); + + void Update(GdkRGBA* color); + void SetColor(const GdkRGBA* color); +#else + static void OnColorChanged(GtkColorSelection* colorselection, + gpointer user_data); // Conversion functions for color static int convertGdkColorComponent(guint16 color_component); static guint16 convertToGdkColorComponent(int color_component); - static GdkColor convertToGdkColor(nscolor color); - static nsString ToHexString(int n); + static GdkColor convertToGdkColor(nscolor color); static GtkColorSelection* WidgetGetColorSelection(GtkWidget* widget); - - void Done(GtkWidget* dialog, gint response_id); + void Update(GtkColorSelection* colorselection); void ReadValueFromColorSelection(GtkColorSelection* colorselection); +#endif + + void Done(GtkWidget* dialog, gint response_id); nsCOMPtr mParentWidget; nsCOMPtr mCallback; From 626e7b7b7c297719b2dbbeca7b557c165dd58e98 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Tue, 1 Sep 2015 10:39:30 -0500 Subject: [PATCH 030/101] Bug 1195472 - Call Show after Enable on puppet widgets to avoid an assertion when running crash tests. r=aklotz --- layout/generic/crashtests/crashtests.list | 2 +- layout/generic/nsPluginFrame.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index fd578d5d0060..b4170e995fef 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -367,10 +367,10 @@ asserts-if(winWidget,0-3) load 499885-1.xhtml load 501535-1.html load 503961-1.xhtml load 503961-2.html -asserts-if(gtkWidget&&browserIsRemote,1) load 505912-1.html # Bug 1195472 load 508168-1.html load 508816-1.xul load 508908-1.html +load 505912-1.html load 509749-1.html load 511482.html load 512724-1.html diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index 9d295e595c58..8c32bfb0d234 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -306,8 +306,8 @@ nsPluginFrame::PrepForDrawing(nsIWidget *aWidget) viewMan->InsertChild(view, mInnerView, nullptr, true); mWidget->SetParent(parentWidget); - mWidget->Show(true); mWidget->Enable(true); + mWidget->Show(true); // Set the plugin window to have an empty clip region until we know // what our true position, size and clip region are. These From 2f23d39464a0e0d23445a6cc1649c51bbf544186 Mon Sep 17 00:00:00 2001 From: Marco Fornaro Date: Tue, 2 Jun 2015 01:40:00 -0400 Subject: [PATCH 031/101] Bug 1168033 - Add a comment to nsHttpConnectionMgr.cpp explaining the assignment of attemptedOptimisticPipeline. r=mcmanus --- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 81a07bf45458..d34ee12b522d 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -1816,6 +1816,7 @@ nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent, if (!attemptedOptimisticPipeline && (classification == nsHttpTransaction::CLASS_REVALIDATION || classification == nsHttpTransaction::CLASS_SCRIPT)) { + // Assignation kept here for documentation purpose; Never read after attemptedOptimisticPipeline = true; if (AddToShortestPipeline(ent, trans, classification, From 03ad9c30eb9ca5e86a60b614b314a47a59ca1eac Mon Sep 17 00:00:00 2001 From: "Nils Ohlmeier [:drno]" Date: Fri, 28 Aug 2015 13:22:45 -0700 Subject: [PATCH 032/101] Bug 1199766 - Disable ICE TCP SO gathering via user pref. r=bwc --HG-- extra : transplant_source : %60%C8%88-%E0%F2%9F%3FMn%A8%7C%3Ah%94s%D2%99%2AX --- media/mtransport/nricectx.cpp | 2 -- modules/libpref/init/all.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/media/mtransport/nricectx.cpp b/media/mtransport/nricectx.cpp index 98b2c2b7bb99..99f3e6014123 100644 --- a/media/mtransport/nricectx.cpp +++ b/media/mtransport/nricectx.cpp @@ -468,8 +468,6 @@ RefPtr NrIceCtx::Create(const std::string& name, ice_trickle_grace_period); NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT, ice_tcp_so_sock_count); - NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_SO_SOCK_COUNT, - ice_tcp_so_sock_count); NR_reg_set_int4((char *)NR_ICE_REG_ICE_TCP_LISTEN_BACKLOG, ice_tcp_listen_backlog); diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index c6836365625d..5c322d855887 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -397,6 +397,7 @@ pref("media.navigator.permission.disabled", false); pref("media.peerconnection.default_iceservers", "[]"); pref("media.peerconnection.ice.loopback", false); // Set only for testing in offline environments. pref("media.peerconnection.ice.tcp", false); +pref("media.peerconnection.ice.tcp_so_sock_count", 0); // Disable SO gathering pref("media.peerconnection.ice.link_local", false); // Set only for testing IPV6 in networks that don't assign IPV6 addresses pref("media.peerconnection.ice.force_interface", ""); // Limit to only a single interface pref("media.peerconnection.ice.relay_only", false); // Limit candidates to TURN From 28b858ab4a2b7c9b431f735bb8bf5834637c9995 Mon Sep 17 00:00:00 2001 From: John Daggett Date: Wed, 2 Sep 2015 10:58:23 +0900 Subject: [PATCH 033/101] Bug 1100949 - wrap font info reads with structured exception handler. r=bas --- gfx/thebes/gfxFontInfoLoader.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/gfx/thebes/gfxFontInfoLoader.cpp b/gfx/thebes/gfxFontInfoLoader.cpp index 3b33725495c0..90d18f6f1c20 100644 --- a/gfx/thebes/gfxFontInfoLoader.cpp +++ b/gfx/thebes/gfxFontInfoLoader.cpp @@ -26,7 +26,14 @@ FontInfoData::Load() uint32_t i, n = mFontFamiliesToLoad.Length(); mLoadStats.families = n; for (i = 0; i < n; i++) { - LoadFontFamilyData(mFontFamiliesToLoad[i]); + // font file memory mapping sometimes causes exceptions - bug 1100949 + MOZ_SEH_TRY { + LoadFontFamilyData(mFontFamiliesToLoad[i]); + } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + gfxCriticalError() << + "Exception occurred reading font data for " << + NS_ConvertUTF16toUTF8(mFontFamiliesToLoad[i]).get(); + } } mLoadTime = TimeStamp::Now() - start; From 033cb8c72b653a51519c0a74df9f90de9a7afdf4 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Tue, 1 Sep 2015 06:17:00 -0400 Subject: [PATCH 034/101] Bug 1092125 - Part 1: Add non-scaling-stroke support to nsSVGPathGeometryElement::GetGeometryBounds (except line). r=jwatt --- dom/svg/SVGCircleElement.cpp | 22 +++++++++++---- dom/svg/SVGCircleElement.h | 3 ++- dom/svg/SVGContentUtils.cpp | 39 +++++++++++++++++++++++++++ dom/svg/SVGContentUtils.h | 24 ++++++++++++++--- dom/svg/SVGEllipseElement.cpp | 22 +++++++++++---- dom/svg/SVGEllipseElement.h | 3 ++- dom/svg/SVGImageElement.cpp | 8 +++--- dom/svg/SVGImageElement.h | 3 ++- dom/svg/SVGLineElement.cpp | 22 +++++++++------ dom/svg/SVGLineElement.h | 3 ++- dom/svg/SVGRectElement.cpp | 30 +++++++++++++++++---- dom/svg/SVGRectElement.h | 3 ++- dom/svg/nsSVGPathGeometryElement.h | 11 +++++++- dom/svg/nsSVGPolyElement.cpp | 18 +++++++------ dom/svg/nsSVGPolyElement.h | 3 ++- dom/svg/test/bounds-helper.svg | 8 ++++++ dom/svg/test/test_bounds.html | 33 ++++++++++++++++++----- layout/svg/nsSVGPathGeometryFrame.cpp | 37 ++++++++++++++++--------- 18 files changed, 228 insertions(+), 64 deletions(-) diff --git a/dom/svg/SVGCircleElement.cpp b/dom/svg/SVGCircleElement.cpp index 9a39f213acb3..336ae0a1b887 100644 --- a/dom/svg/SVGCircleElement.cpp +++ b/dom/svg/SVGCircleElement.cpp @@ -83,26 +83,38 @@ SVGCircleElement::GetLengthInfo() // nsSVGPathGeometryElement methods bool -SVGCircleElement::GetGeometryBounds( - Rect* aBounds, const StrokeOptions& aStrokeOptions, const Matrix& aTransform) +SVGCircleElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { float x, y, r; GetAnimatedLengthValues(&x, &y, &r, nullptr); if (r <= 0.f) { // Rendering of the element is disabled - *aBounds = Rect(aTransform * Point(x, y), Size()); + *aBounds = Rect(aToBoundsSpace * Point(x, y), Size()); return true; } - if (aTransform.IsRectilinear()) { + if (aToBoundsSpace.IsRectilinear()) { // Optimize the case where we can treat the circle as a rectangle and // still get tight bounds. if (aStrokeOptions.mLineWidth > 0.f) { + if (aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace->IsRectilinear()) { + Rect userBounds(x - r, y - r, 2 * r, 2 * r); + SVGContentUtils::RectilinearGetStrokeBounds( + userBounds, aToBoundsSpace, *aToNonScalingStrokeSpace, + aStrokeOptions.mLineWidth, aBounds); + return true; + } + return false; + } r += aStrokeOptions.mLineWidth / 2.f; } Rect rect(x - r, y - r, 2 * r, 2 * r); - *aBounds = aTransform.TransformBounds(rect); + *aBounds = aToBoundsSpace.TransformBounds(rect); return true; } diff --git a/dom/svg/SVGCircleElement.h b/dom/svg/SVGCircleElement.h index d0968a7eca89..ff7bfe9024ad 100644 --- a/dom/svg/SVGCircleElement.h +++ b/dom/svg/SVGCircleElement.h @@ -32,7 +32,8 @@ public: // nsSVGPathGeometryElement methods: virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) override; + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; virtual already_AddRefed BuildPath(PathBuilder* aBuilder) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp index 0689e4798a01..b29bedb66e8d 100644 --- a/dom/svg/SVGContentUtils.cpp +++ b/dom/svg/SVGContentUtils.cpp @@ -464,6 +464,45 @@ SVGContentUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM) return GetCTMInternal(aElement, aScreenCTM, false); } +void +SVGContentUtils::RectilinearGetStrokeBounds(const Rect& aRect, + const Matrix& aToBoundsSpace, + const Matrix& aToNonScalingStrokeSpace, + float aStrokeWidth, + Rect* aBounds) +{ + MOZ_ASSERT(aToBoundsSpace.IsRectilinear(), + "aToBoundsSpace must be rectilinear"); + MOZ_ASSERT(aToNonScalingStrokeSpace.IsRectilinear(), + "aToNonScalingStrokeSpace must be rectilinear"); + + Matrix nonScalingToSource = aToNonScalingStrokeSpace.Inverse(); + Matrix nonScalingToBounds = nonScalingToSource * aToBoundsSpace; + + *aBounds = aToBoundsSpace.TransformBounds(aRect); + + // Compute the amounts dx and dy that nonScalingToBounds scales a half-width + // stroke in the x and y directions, and then inflate aBounds by those amounts + // so that when aBounds is transformed back to non-scaling-stroke space + // it will map onto the correct stroked bounds. + + Float dx = 0.0f; + Float dy = 0.0f; + // nonScalingToBounds is rectilinear, so either _12 and _21 are zero or _11 + // and _22 are zero, and in each case the non-zero entries (from among _11, + // _12, _21, _22) simply scale the stroke width in the x and y directions. + if (FuzzyEqual(nonScalingToBounds._12, 0) && + FuzzyEqual(nonScalingToBounds._21, 0)) { + dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._11); + dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._22); + } else { + dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._21); + dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._12); + } + + aBounds->Inflate(dx, dy); +} + double SVGContentUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight) { diff --git a/dom/svg/SVGContentUtils.h b/dom/svg/SVGContentUtils.h index 26f96a7a5c52..2136c5e6e780 100644 --- a/dom/svg/SVGContentUtils.h +++ b/dom/svg/SVGContentUtils.h @@ -63,6 +63,8 @@ class SVGContentUtils { public: typedef mozilla::gfx::Float Float; + typedef mozilla::gfx::Matrix Matrix; + typedef mozilla::gfx::Rect Rect; typedef mozilla::gfx::StrokeOptions StrokeOptions; typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio; typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio; @@ -180,7 +182,23 @@ public: const char16_t **aParams, uint32_t aParamsLength); - static mozilla::gfx::Matrix GetCTM(nsSVGElement *aElement, bool aScreenCTM); + static Matrix GetCTM(nsSVGElement *aElement, bool aScreenCTM); + + /** + * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect + * aRect. + * @param aToBoundsSpace transforms from source space to the space aBounds + * should be computed in. Must be rectilinear. + * @param aToNonScalingStrokeSpace transforms from source + * space to the space in which non-scaling stroke should be applied. + * Must be rectilinear. + */ + static void + RectilinearGetStrokeBounds(const Rect& aRect, + const Matrix& aToBoundsSpace, + const Matrix& aToNonScalingStrokeSpace, + float aStrokeWidth, + Rect* aBounds); /** * Check if this is one of the SVG elements that SVG 1.1 Full says @@ -205,13 +223,13 @@ public: /* Generate a viewbox to viewport tranformation matrix */ - static mozilla::gfx::Matrix + static Matrix GetViewBoxTransform(float aViewportWidth, float aViewportHeight, float aViewboxX, float aViewboxY, float aViewboxWidth, float aViewboxHeight, const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio); - static mozilla::gfx::Matrix + static Matrix GetViewBoxTransform(float aViewportWidth, float aViewportHeight, float aViewboxX, float aViewboxY, float aViewboxWidth, float aViewboxHeight, diff --git a/dom/svg/SVGEllipseElement.cpp b/dom/svg/SVGEllipseElement.cpp index e0e5c9bb552e..5a5b483ba0c5 100644 --- a/dom/svg/SVGEllipseElement.cpp +++ b/dom/svg/SVGEllipseElement.cpp @@ -94,27 +94,39 @@ SVGEllipseElement::GetLengthInfo() // nsSVGPathGeometryElement methods bool -SVGEllipseElement::GetGeometryBounds( - Rect* aBounds, const StrokeOptions& aStrokeOptions, const Matrix& aTransform) +SVGEllipseElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { float x, y, rx, ry; GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr); if (rx <= 0.f || ry <= 0.f) { // Rendering of the element is disabled - *aBounds = Rect(aTransform * Point(x, y), Size()); + *aBounds = Rect(aToBoundsSpace * Point(x, y), Size()); return true; } - if (aTransform.IsRectilinear()) { + if (aToBoundsSpace.IsRectilinear()) { // Optimize the case where we can treat the ellipse as a rectangle and // still get tight bounds. if (aStrokeOptions.mLineWidth > 0.f) { + if (aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace->IsRectilinear()) { + Rect userBounds(x - rx, y - ry, 2 * rx, 2 * ry); + SVGContentUtils::RectilinearGetStrokeBounds( + userBounds, aToBoundsSpace, *aToNonScalingStrokeSpace, + aStrokeOptions.mLineWidth, aBounds); + return true; + } + return false; + } rx += aStrokeOptions.mLineWidth / 2.f; ry += aStrokeOptions.mLineWidth / 2.f; } Rect rect(x - rx, y - ry, 2 * rx, 2 * ry); - *aBounds = aTransform.TransformBounds(rect); + *aBounds = aToBoundsSpace.TransformBounds(rect); return true; } diff --git a/dom/svg/SVGEllipseElement.h b/dom/svg/SVGEllipseElement.h index b9efe7f9bf84..27c132fd9674 100644 --- a/dom/svg/SVGEllipseElement.h +++ b/dom/svg/SVGEllipseElement.h @@ -32,7 +32,8 @@ public: // nsSVGPathGeometryElement methods: virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) override; + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; virtual already_AddRefed BuildPath(PathBuilder* aBuilder) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/svg/SVGImageElement.cpp b/dom/svg/SVGImageElement.cpp index b05a756703d0..a2224e326d80 100644 --- a/dom/svg/SVGImageElement.cpp +++ b/dom/svg/SVGImageElement.cpp @@ -226,8 +226,10 @@ SVGImageElement::IsAttributeMapped(const nsIAtom* name) const /* For the purposes of the update/invalidation logic pretend to be a rectangle. */ bool -SVGImageElement::GetGeometryBounds( - Rect* aBounds, const StrokeOptions& aStrokeOptions, const Matrix& aTransform) +SVGImageElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { Rect rect; GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width, @@ -238,7 +240,7 @@ SVGImageElement::GetGeometryBounds( rect.SetEmpty(); // Make sure width/height are zero and not negative } - *aBounds = aTransform.TransformBounds(rect); + *aBounds = aToBoundsSpace.TransformBounds(rect); return true; } diff --git a/dom/svg/SVGImageElement.h b/dom/svg/SVGImageElement.h index f8e2882da3ad..69ef9c7d2e10 100644 --- a/dom/svg/SVGImageElement.h +++ b/dom/svg/SVGImageElement.h @@ -55,7 +55,8 @@ public: // nsSVGPathGeometryElement methods: virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) override; + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; virtual already_AddRefed BuildPath(PathBuilder* aBuilder) override; // nsSVGSVGElement methods: diff --git a/dom/svg/SVGLineElement.cpp b/dom/svg/SVGLineElement.cpp index 8e481567f8f6..eb6d4a3b8005 100644 --- a/dom/svg/SVGLineElement.cpp +++ b/dom/svg/SVGLineElement.cpp @@ -148,27 +148,33 @@ SVGLineElement::BuildPath(PathBuilder* aBuilder) } bool -SVGLineElement::GetGeometryBounds( - Rect* aBounds, const StrokeOptions& aStrokeOptions, const Matrix& aTransform) +SVGLineElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace) { + return false; + } + float x1, y1, x2, y2; GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); if (aStrokeOptions.mLineWidth <= 0) { - *aBounds = Rect(aTransform * Point(x1, y1), Size()); - aBounds->ExpandToEnclose(aTransform * Point(x2, y2)); + *aBounds = Rect(aToBoundsSpace * Point(x1, y1), Size()); + aBounds->ExpandToEnclose(aToBoundsSpace * Point(x2, y2)); return true; } if (aStrokeOptions.mLineCap == CapStyle::ROUND) { - if (!aTransform.IsRectilinear()) { + if (!aToBoundsSpace.IsRectilinear()) { // TODO: handle this case. return false; } Rect bounds(Point(x1, y1), Size()); bounds.ExpandToEnclose(Point(x2, y2)); bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); - *aBounds = aTransform.TransformBounds(bounds); + *aBounds = aToBoundsSpace.TransformBounds(bounds); return true; } @@ -201,9 +207,9 @@ SVGLineElement::GetGeometryBounds( points[2] = Point(x2 + xDelta, y2 + yDelta); points[3] = Point(x2 - xDelta, y2 - yDelta); - *aBounds = Rect(aTransform * points[0], Size()); + *aBounds = Rect(aToBoundsSpace * points[0], Size()); for (uint32_t i = 1; i < 4; ++i) { - aBounds->ExpandToEnclose(aTransform * points[i]); + aBounds->ExpandToEnclose(aToBoundsSpace * points[i]); } return true; } diff --git a/dom/svg/SVGLineElement.h b/dom/svg/SVGLineElement.h index 93ae940130bf..f2bafa4a36b9 100644 --- a/dom/svg/SVGLineElement.h +++ b/dom/svg/SVGLineElement.h @@ -40,7 +40,8 @@ public: virtual void GetAsSimplePath(SimplePath* aSimplePath) override; virtual already_AddRefed BuildPath(PathBuilder* aBuilder) override; virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) override; + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; diff --git a/dom/svg/SVGRectElement.cpp b/dom/svg/SVGRectElement.cpp index 923ecbfbcf76..9fd31175eb5a 100644 --- a/dom/svg/SVGRectElement.cpp +++ b/dom/svg/SVGRectElement.cpp @@ -112,8 +112,10 @@ SVGRectElement::GetLengthInfo() // nsSVGPathGeometryElement methods bool -SVGRectElement::GetGeometryBounds( - Rect* aBounds, const StrokeOptions& aStrokeOptions, const Matrix& aTransform) +SVGRectElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { Rect rect; Float rx, ry; @@ -124,11 +126,11 @@ SVGRectElement::GetGeometryBounds( // Rendering of the element disabled rect.SetEmpty(); // Make sure width/height are zero and not negative // We still want the x/y position from 'rect' - *aBounds = aTransform.TransformBounds(rect); + *aBounds = aToBoundsSpace.TransformBounds(rect); return true; } - if (!aTransform.IsRectilinear()) { + if (!aToBoundsSpace.IsRectilinear()) { // We can't ignore the radii in this case if we want tight bounds rx = std::max(rx, 0.0f); ry = std::max(ry, 0.0f); @@ -139,10 +141,28 @@ SVGRectElement::GetGeometryBounds( } if (aStrokeOptions.mLineWidth > 0.f) { + if (aToNonScalingStrokeSpace) { + if (aToNonScalingStrokeSpace->IsRectilinear()) { + rect = aToNonScalingStrokeSpace->TransformBounds(rect); + // Note that, in principle, an author could cause the corners of the + // rect to be beveled by specifying stroke-linejoin or setting + // stroke-miterlimit to be less than sqrt(2). In that very unlikely + // event the bounds that we calculate here may be too big if + // aToBoundsSpace is non-rectilinear. This is likely to be so rare it's + // not worth handling though. + rect.Inflate(aStrokeOptions.mLineWidth / 2.f); + Matrix nonScalingToBounds = + aToNonScalingStrokeSpace->Inverse() * aToBoundsSpace; + *aBounds = nonScalingToBounds.TransformBounds(rect); + return true; + } + return false; + } + // The "beveled" comment above applies here too rect.Inflate(aStrokeOptions.mLineWidth / 2.f); } - *aBounds = aTransform.TransformBounds(rect); + *aBounds = aToBoundsSpace.TransformBounds(rect); return true; } diff --git a/dom/svg/SVGRectElement.h b/dom/svg/SVGRectElement.h index 67d5c1384e50..fe32414eec5f 100644 --- a/dom/svg/SVGRectElement.h +++ b/dom/svg/SVGRectElement.h @@ -32,7 +32,8 @@ public: // nsSVGPathGeometryElement methods: virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) override; + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; virtual void GetAsSimplePath(SimplePath* aSimplePath) override; virtual already_AddRefed BuildPath(PathBuilder* aBuilder = nullptr) override; diff --git a/dom/svg/nsSVGPathGeometryElement.h b/dom/svg/nsSVGPathGeometryElement.h index e112b108613d..ae45374896eb 100644 --- a/dom/svg/nsSVGPathGeometryElement.h +++ b/dom/svg/nsSVGPathGeometryElement.h @@ -76,9 +76,18 @@ public: * GetStrokedBounds on it. It also helps us avoid rounding error for simple * shapes and simple transforms where the Moz2D Path backends can fail to * produce the clean integer bounds that content authors expect in some cases. + * + * If |aToNonScalingStrokeSpace| is non-null then |aBounds|, which is computed + * in bounds space, has the property that it's the smallest (axis-aligned) + * rectangular bound containing the image of this shape as stroked in + * non-scaling-stroke space. (When all transforms involved are rectilinear + * the bounds of the image of |aBounds| in non-scaling-stroke space will be + * tight, but if there are non-rectilinear transforms involved then that may + * be impossible and this method will return false). */ virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) { + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) { return false; } diff --git a/dom/svg/nsSVGPolyElement.cpp b/dom/svg/nsSVGPolyElement.cpp index 0061a35df1f9..5686a0cbd5f7 100644 --- a/dom/svg/nsSVGPolyElement.cpp +++ b/dom/svg/nsSVGPolyElement.cpp @@ -122,8 +122,10 @@ nsSVGPolyElement::GetMarkPoints(nsTArray *aMarks) } bool -nsSVGPolyElement::GetGeometryBounds( - Rect* aBounds, const StrokeOptions& aStrokeOptions, const Matrix& aTransform) +nsSVGPolyElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { const SVGPointList &points = mPoints.GetAnimValue(); @@ -133,23 +135,23 @@ nsSVGPolyElement::GetGeometryBounds( return true; } - if (aStrokeOptions.mLineWidth > 0) { - // We don't handle stroke-miterlimit etc. yet + if (aStrokeOptions.mLineWidth > 0 || aToNonScalingStrokeSpace) { + // We don't handle non-scaling-stroke or stroke-miterlimit etc. yet return false; } - if (aTransform.IsRectilinear()) { + if (aToBoundsSpace.IsRectilinear()) { // We can avoid transforming each point and just transform the result. // Important for large point lists. Rect bounds(points[0], Size()); for (uint32_t i = 1; i < points.Length(); ++i) { bounds.ExpandToEnclose(points[i]); } - *aBounds = aTransform.TransformBounds(bounds); + *aBounds = aToBoundsSpace.TransformBounds(bounds); } else { - *aBounds = Rect(aTransform * points[0], Size()); + *aBounds = Rect(aToBoundsSpace * points[0], Size()); for (uint32_t i = 1; i < points.Length(); ++i) { - aBounds->ExpandToEnclose(aTransform * points[i]); + aBounds->ExpandToEnclose(aToBoundsSpace * points[i]); } } return true; diff --git a/dom/svg/nsSVGPolyElement.h b/dom/svg/nsSVGPolyElement.h index ffcb145c7384..1dcde0078450 100644 --- a/dom/svg/nsSVGPolyElement.h +++ b/dom/svg/nsSVGPolyElement.h @@ -47,7 +47,8 @@ public: virtual bool IsMarkable() override { return true; } virtual void GetMarkPoints(nsTArray *aMarks) override; virtual bool GetGeometryBounds(Rect* aBounds, const StrokeOptions& aStrokeOptions, - const Matrix& aTransform) override; + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace = nullptr) override; // WebIDL already_AddRefed Points(); diff --git a/dom/svg/test/bounds-helper.svg b/dom/svg/test/bounds-helper.svg index 6e08ba8f6f87..98100ee0cc37 100644 --- a/dom/svg/test/bounds-helper.svg +++ b/dom/svg/test/bounds-helper.svg @@ -39,5 +39,13 @@ text { font: 20px monospace; } + + diff --git a/dom/svg/test/test_bounds.html b/dom/svg/test/test_bounds.html index e615dfd6de37..7128a97011b8 100644 --- a/dom/svg/test/test_bounds.html +++ b/dom/svg/test/test_bounds.html @@ -159,13 +159,10 @@ function runTest() is(rect3aBounds.width, 108, "rect3a.getBoundingClientRect().width"); is(rect3aBounds.height, 108, "rect3a.getBoundingClientRect().height"); - // Our PathExtentsToMaxStrokeExtents implementation considers the stroke - // width to be sqrt(2)*stroke-width in case the rect is rotated 45 degrees, - // so unfortunately we get slightly large results currently. Bug 1092125. - isWithAbsTolerance(rect3bBounds.left, 198, 1, "rect3b.getBoundingClientRect().left"); - isWithAbsTolerance(rect3bBounds.top, 198, 1, "rect3b.getBoundingClientRect().top"); - isWithAbsTolerance(rect3bBounds.width, 54, 2, "rect3b.getBoundingClientRect().width"); - isWithAbsTolerance(rect3bBounds.height, 54, 2, "rect3b.getBoundingClientRect().height"); + isWithAbsTolerance(rect3bBounds.left, 198, 0.1, "rect3b.getBoundingClientRect().left"); + isWithAbsTolerance(rect3bBounds.top, 198, 0.1, "rect3b.getBoundingClientRect().top"); + isWithAbsTolerance(rect3bBounds.width, 54, 0.1, "rect3b.getBoundingClientRect().width"); + isWithAbsTolerance(rect3bBounds.height, 54, 0.1, "rect3b.getBoundingClientRect().height"); rect = new Rect(350 - 108 * sin45, 150 - 108 * sin45, 108 * sin45 * 2, 108 * sin45 * 2); isWithAbsTolerance(rect4aBounds.left, rect.left, 0.1, "rect4a.getBoundingClientRect().left"); @@ -203,6 +200,28 @@ function runTest() is(gBounds.width, 50, "g2.getBoundingClientRect().width"); is(gBounds.height, 50, "g2.getBoundingClientRect().height"); + var nonScalingStrokedCircle1Bounds = + doc.getElementById("nonScalingStrokedCircle1").getBoundingClientRect(); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.left, 10, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.top, 105, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.width, 70, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedCircle1Bounds.height, 50, 0.15, + "nonScalingStrokedCircle1.getBoundingClientRect().height"); + + var nonScalingStrokedEllipse1Bounds = + doc.getElementById("nonScalingStrokedEllipse1").getBoundingClientRect(); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.left, 5, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.top, 40, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.width, 30, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.height, 40, 0.15, + "nonScalingStrokedEllipse1.getBoundingClientRect().height"); + SimpleTest.finish(); } diff --git a/layout/svg/nsSVGPathGeometryFrame.cpp b/layout/svg/nsSVGPathGeometryFrame.cpp index 83eb1346d502..1448ed236001 100644 --- a/layout/svg/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/nsSVGPathGeometryFrame.cpp @@ -470,25 +470,36 @@ nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, ((aFlags & nsSVGUtils::eBBoxIncludeStroke) && nsSVGUtils::HasStroke(this)); - bool gotSimpleBounds = false; - if (!StyleSVGReset()->HasNonScalingStroke()) { - SVGContentUtils::AutoStrokeOptions strokeOptions; + SVGContentUtils::AutoStrokeOptions strokeOptions; + if (getStroke) { + SVGContentUtils::GetStrokeOptions(&strokeOptions, element, + StyleContext(), nullptr, + SVGContentUtils::eIgnoreStrokeDashing); + } else { + // Override the default line width of 1.f so that when we call + // GetGeometryBounds below the result doesn't include stroke bounds. strokeOptions.mLineWidth = 0.f; - if (getStroke) { - SVGContentUtils::GetStrokeOptions(&strokeOptions, element, - StyleContext(), nullptr, - SVGContentUtils::eIgnoreStrokeDashing); - } - Rect simpleBounds; + } + + Rect simpleBounds; + bool gotSimpleBounds = false; + gfxMatrix userToOuterSVG; + if (getStroke && + nsSVGUtils::GetNonScalingStrokeTransform(this, &userToOuterSVG)) { + Matrix moz2dUserToOuterSVG = ToMatrix(userToOuterSVG); + gotSimpleBounds = element->GetGeometryBounds(&simpleBounds, + strokeOptions, + aToBBoxUserspace, + &moz2dUserToOuterSVG); + } else { gotSimpleBounds = element->GetGeometryBounds(&simpleBounds, strokeOptions, aToBBoxUserspace); - if (gotSimpleBounds) { - bbox = simpleBounds; - } } - if (!gotSimpleBounds) { + if (gotSimpleBounds) { + bbox = simpleBounds; + } else { // Get the bounds using a Moz2D Path object (more expensive): RefPtr tmpDT; #ifdef XP_WIN From 051b2f948365576ad3c9aaed1afd04dfadfed4e4 Mon Sep 17 00:00:00 2001 From: Tom Klein Date: Wed, 19 Aug 2015 14:48:00 -0400 Subject: [PATCH 035/101] Bug 1092125 - Part 2: Add non-scaling-stroke support to SVGLineElement::GetGeometryBounds. r=jwatt --- dom/svg/SVGLineElement.cpp | 70 ++++++++++++++++++++++++++-------- dom/svg/test/bounds-helper.svg | 12 ++++++ dom/svg/test/test_bounds.html | 39 +++++++++++++++++++ layout/svg/nsSVGUtils.cpp | 2 +- 4 files changed, 106 insertions(+), 17 deletions(-) diff --git a/dom/svg/SVGLineElement.cpp b/dom/svg/SVGLineElement.cpp index eb6d4a3b8005..e9f0587e5671 100644 --- a/dom/svg/SVGLineElement.cpp +++ b/dom/svg/SVGLineElement.cpp @@ -153,10 +153,6 @@ SVGLineElement::GetGeometryBounds(Rect* aBounds, const Matrix& aToBoundsSpace, const Matrix* aToNonScalingStrokeSpace) { - if (aToNonScalingStrokeSpace) { - return false; - } - float x1, y1, x2, y2; GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); @@ -166,21 +162,53 @@ SVGLineElement::GetGeometryBounds(Rect* aBounds, return true; } + // transform from non-scaling-stroke space to the space in which we compute + // bounds + Matrix nonScalingToBounds; + if (aToNonScalingStrokeSpace) { + Matrix nonScalingToUser = aToNonScalingStrokeSpace->Inverse(); + nonScalingToBounds = nonScalingToUser * aToBoundsSpace; + } + if (aStrokeOptions.mLineCap == CapStyle::ROUND) { - if (!aToBoundsSpace.IsRectilinear()) { + if (!aToBoundsSpace.IsRectilinear() || + (aToNonScalingStrokeSpace && + !aToNonScalingStrokeSpace->IsRectilinear())) { // TODO: handle this case. return false; } Rect bounds(Point(x1, y1), Size()); bounds.ExpandToEnclose(Point(x2, y2)); - bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); - *aBounds = aToBoundsSpace.TransformBounds(bounds); + if (aToNonScalingStrokeSpace) { + bounds = aToNonScalingStrokeSpace->TransformBounds(bounds); + bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); + *aBounds = nonScalingToBounds.TransformBounds(bounds); + } else { + bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); + *aBounds = aToBoundsSpace.TransformBounds(bounds); + } return true; } + // Handle butt and square linecap, normal and non-scaling stroke cases + // together: start with endpoints (x1, y1), (x2, y2) in the stroke space, + // compute the four corners of the stroked line, transform the corners to + // bounds space, and compute bounds there. + + if (aToNonScalingStrokeSpace) { + Point nonScalingSpaceP1, nonScalingSpaceP2; + nonScalingSpaceP1 = *aToNonScalingStrokeSpace * Point(x1, y1); + nonScalingSpaceP2 = *aToNonScalingStrokeSpace * Point(x2, y2); + x1 = nonScalingSpaceP1.x; + y1 = nonScalingSpaceP1.y; + x2 = nonScalingSpaceP2.x; + y2 = nonScalingSpaceP2.y; + } + Float length = Float(NS_hypot(x2 - x1, y2 - y1)); Float xDelta; Float yDelta; + Point points[4]; if (aStrokeOptions.mLineCap == CapStyle::BUTT) { if (length == 0.f) { @@ -190,27 +218,37 @@ SVGLineElement::GetGeometryBounds(Rect* aBounds, xDelta = ratio * (y2 - y1); yDelta = ratio * (x2 - x1); } + points[0] = Point(x1 - xDelta, y1 + yDelta); + points[1] = Point(x1 + xDelta, y1 - yDelta); + points[2] = Point(x2 + xDelta, y2 - yDelta); + points[3] = Point(x2 - xDelta, y2 + yDelta); } else { MOZ_ASSERT(aStrokeOptions.mLineCap == CapStyle::SQUARE); if (length == 0.f) { xDelta = yDelta = aStrokeOptions.mLineWidth / 2.f; + points[0] = Point(x1 - xDelta, y1 + yDelta); + points[1] = Point(x1 - xDelta, y1 - yDelta); + points[2] = Point(x1 + xDelta, y1 - yDelta); + points[3] = Point(x1 + xDelta, y1 + yDelta); } else { Float ratio = aStrokeOptions.mLineWidth / 2.f / length; - xDelta = yDelta = ratio * (fabs(y2 - y1) + fabs(x2 - x1)); + yDelta = ratio * (x2 - x1); + xDelta = ratio * (y2 - y1); + points[0] = Point(x1 - yDelta - xDelta, y1 - xDelta + yDelta); + points[1] = Point(x1 - yDelta + xDelta, y1 - xDelta - yDelta); + points[2] = Point(x2 + yDelta + xDelta, y2 + xDelta - yDelta); + points[3] = Point(x2 + yDelta - xDelta, y2 + xDelta + yDelta); } } - Point points[4]; + const Matrix& toBoundsSpace = aToNonScalingStrokeSpace ? + nonScalingToBounds : aToBoundsSpace; - points[0] = Point(x1 - xDelta, y1 - yDelta); - points[1] = Point(x1 + xDelta, y1 + yDelta); - points[2] = Point(x2 + xDelta, y2 + yDelta); - points[3] = Point(x2 - xDelta, y2 - yDelta); - - *aBounds = Rect(aToBoundsSpace * points[0], Size()); + *aBounds = Rect(toBoundsSpace * points[0], Size()); for (uint32_t i = 1; i < 4; ++i) { - aBounds->ExpandToEnclose(aToBoundsSpace * points[i]); + aBounds->ExpandToEnclose(toBoundsSpace * points[i]); } + return true; } diff --git a/dom/svg/test/bounds-helper.svg b/dom/svg/test/bounds-helper.svg index 98100ee0cc37..6fc33b1ecb3e 100644 --- a/dom/svg/test/bounds-helper.svg +++ b/dom/svg/test/bounds-helper.svg @@ -47,5 +47,17 @@ text { font: 20px monospace; } transform="matrix(0 3 -2 0 0 0)" fill="none" stroke="steelblue" stroke-width="10" vector-effect="non-scaling-stroke" /> + + + diff --git a/dom/svg/test/test_bounds.html b/dom/svg/test/test_bounds.html index 7128a97011b8..bc1801821ae7 100644 --- a/dom/svg/test/test_bounds.html +++ b/dom/svg/test/test_bounds.html @@ -222,6 +222,45 @@ function runTest() isWithAbsTolerance(nonScalingStrokedEllipse1Bounds.height, 40, 0.15, "nonScalingStrokedEllipse1.getBoundingClientRect().height"); + var nonScalingStrokedLine1Bounds = + doc.getElementById("nonScalingStrokedLine1").getBoundingClientRect(); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.left, 235, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.top, 10, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.width, 10, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedLine1Bounds.height, 25, 0.1, + "nonScalingStrokedLine1.getBoundingClientRect().height"); + + var nonScalingStrokedLine2Bounds = + doc.getElementById("nonScalingStrokedLine2").getBoundingClientRect(); + var capDelta = 5/Math.SQRT2 + 5/Math.SQRT2; + rect = new Rect(260 - capDelta, 15 - capDelta, 20/Math.SQRT2 + 2 * capDelta, + 20/Math.SQRT2 + 2 * capDelta); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.left, rect.left, 0.1, + "nonScalingStrokedLine2.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.top, rect.top, 0.1, + "nonScalingStrokedLine2.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.width, rect.width, 0.1, + "nonScalingStrokedLine2.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedLine2Bounds.height, rect.height, 0.1, + "nonScalingStrokedLine2.getBoundingClientRect().height"); + + var nonScalingStrokedLine3Bounds = + doc.getElementById("nonScalingStrokedLine3").getBoundingClientRect(); + var capDelta = 5/Math.SQRT2; + rect = new Rect(280 - capDelta, 15 - capDelta, 20/Math.SQRT2 + 2 * capDelta, + 20/Math.SQRT2 + 2 * capDelta); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.left, rect.left, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().left"); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.top, rect.top, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().top"); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.width, rect.width, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().width"); + isWithAbsTolerance(nonScalingStrokedLine3Bounds.height, rect.height, 0.1, + "nonScalingStrokedLine3.getBoundingClientRect().height"); + SimpleTest.finish(); } diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 47776437eede..e3b50b1f7cf1 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -1171,7 +1171,7 @@ PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, gfxMatrix outerSVGToUser; if (nsSVGUtils::GetNonScalingStrokeTransform(aFrame, &outerSVGToUser)) { outerSVGToUser.Invert(); - matrix *= outerSVGToUser; + matrix.PreMultiply(outerSVGToUser); } double dx = style_expansion * (fabs(matrix._11) + fabs(matrix._21)); From 180a94bf7c33f72a5c46f01011a3e051d2c81784 Mon Sep 17 00:00:00 2001 From: Kalpesh Krishna Date: Tue, 1 Sep 2015 01:09:00 -0400 Subject: [PATCH 036/101] Bug 1198387 - Remove use-cache preference and its references. r=mcmanus --- modules/libpref/init/all.js | 3 --- netwerk/protocol/http/nsHttpChannel.cpp | 4 ---- netwerk/protocol/http/nsHttpHandler.cpp | 8 -------- netwerk/protocol/http/nsHttpHandler.h | 2 -- 4 files changed, 17 deletions(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 5c322d855887..4770a37172ff 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1252,9 +1252,6 @@ pref("network.http.proxy.version", "1.1"); // default // pref("network.http.proxy.version", "1.0"); // uncomment this out in case of problems // (required if using junkbuster proxy) -// enable caching of http documents -pref("network.http.use-cache", true); - // this preference can be set to override the socket type used for normal // HTTP traffic. an empty value indicates the normal TCP/IP socket type. pref("network.http.default-socket-type", ""); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 86c0105012af..2635e31a977f 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -416,10 +416,6 @@ nsHttpChannel::Connect() return NS_ERROR_DOCUMENT_NOT_CACHED; } - if (!gHttpHandler->UseCache()) { - return ContinueConnect(); - } - // open a cache entry for this channel... rv = OpenCacheEntry(isHttps); diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index 81f7f1297800..2c966817e094 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -174,7 +174,6 @@ nsHttpHandler::nsHttpHandler() , mProduct("Gecko") , mCompatFirefoxEnabled(false) , mUserAgentIsDirty(true) - , mUseCache(true) , mPromptTempRedirect(true) , mSendSecureXSiteReferrer(true) , mEnablePersistentHttpsCaching(false) @@ -1154,13 +1153,6 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref) SetAcceptEncodings(acceptEncodings); } - if (PREF_CHANGED(HTTP_PREF("use-cache"))) { - rv = prefs->GetBoolPref(HTTP_PREF("use-cache"), &cVar); - if (NS_SUCCEEDED(rv)) { - mUseCache = cVar; - } - } - if (PREF_CHANGED(HTTP_PREF("default-socket-type"))) { nsXPIDLCString sval; rv = prefs->GetCharPref(HTTP_PREF("default-socket-type"), diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index d60f05fd11d7..c4f49ab49bd6 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -167,7 +167,6 @@ public: nsHttpConnectionMgr *ConnMgr() { return mConnMgr; } // cache support - bool UseCache() const { return mUseCache; } uint32_t GenerateUniqueID() { return ++mLastUniqueID; } uint32_t SessionStartTime() { return mSessionStartTime; } @@ -458,7 +457,6 @@ private: nsXPIDLCString mUserAgentOverride; bool mUserAgentIsDirty; // true if mUserAgent should be rebuilt - bool mUseCache; bool mPromptTempRedirect; // mSendSecureXSiteReferrer: default is false, From 2fd8996148dac1763bef21b1762cd0f26edce5f3 Mon Sep 17 00:00:00 2001 From: Arthur Edelstein Date: Tue, 1 Sep 2015 08:51:00 -0400 Subject: [PATCH 037/101] Bug 1193593 - Test fingerprinting resistance for media queries in picture elements. r=heycam Based on Tor Browser #16315 https://trac.torproject.org/projects/tor/ticket/16315 --- layout/style/test/chrome/bug418986-2.js | 105 ++++++++++++------ layout/style/test/chrome/chrome.ini | 2 + layout/style/test/chrome/match.png | Bin 0 -> 1210 bytes layout/style/test/chrome/mismatch.png | Bin 0 -> 1573 bytes layout/style/test/chrome/test_bug418986-2.xul | 7 +- layout/style/test/mochitest.ini | 2 + layout/style/test/test_bug418986-2.html | 6 +- 7 files changed, 84 insertions(+), 38 deletions(-) create mode 100644 layout/style/test/chrome/match.png create mode 100644 layout/style/test/chrome/mismatch.png diff --git a/layout/style/test/chrome/bug418986-2.js b/layout/style/test/chrome/bug418986-2.js index d3fc28096b59..a8a214019b66 100644 --- a/layout/style/test/chrome/bug418986-2.js +++ b/layout/style/test/chrome/bug418986-2.js @@ -4,8 +4,6 @@ /* jshint loopfunc:true */ /* global window, screen, ok, SpecialPowers, matchMedia */ -SimpleTest.waitForExplicitFinish(); - // Expected values. Format: [name, pref_off_value, pref_on_value] // If pref_*_value is an array with two values, then we will match // any value in between those two values. If a value is null, then @@ -166,19 +164,22 @@ let cssLine = function (query, clazz, id, color) { " { background-color: " + color + "; } }\n"; }; +// __constructQuery(key, val)__. +// Creates a CSS media query from key and val. If key is an array of +// two elements, constructs a range query (using min- and max-). +let constructQuery = function (key, val) { + return Array.isArray(val) ? + "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")" : + "(" + key + ": " + val + ")"; +}; + // __mediaQueryCSSLine(key, val, color)__. // Creates a line containing a CSS media query and a CSS expression. let mediaQueryCSSLine = function (key, val, color) { if (val === null) { return ""; } - let query; - if (Array.isArray(val)) { - query = "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")"; - } else { - query = "(" + key + ": " + val + ")"; - } - return cssLine(query, "spoof", key, color); + return cssLine(constructQuery(key, val), "spoof", key, color); }; // __suppressedMediaQueryCSSLine(key, color)__. @@ -248,33 +249,67 @@ let testOSXFontSmoothing = function (resisting) { "-moz-osx-font-smoothing"); }; -// An iterator yielding pref values for two consecutive tests. -let prefVals = (for (prefVal of [false, true]) prefVal); +// __sleep(timeoutMs)__. +// Returns a promise that resolves after the given timeout. +let sleep = function (timeoutMs) { + return new Promise(function(resolve, reject) { + window.setTimeout(resolve); + }); +}; + +// __testMediaQueriesInPictureElements(resisting)__. +// Test to see if media queries are properly spoofed in picture elements +// when we are resisting fingerprinting. A generator function +// to be used with SpawnTask.js. +let testMediaQueriesInPictureElements = function* (resisting) { + let lines = ""; + for (let [key, offVal, onVal] of expected_values) { + let expected = resisting ? onVal : offVal; + if (expected) { + let query = constructQuery(key, expected); + lines += "\n"; + lines += " \n"; + lines += " " + key + "\n"; + lines += "
\n"; + } + } + document.getElementById("pictures").innerHTML = lines; + var testImages = document.getElementsByClassName("testImage"); + yield sleep(0); + for (let testImage of testImages) { + ok(testImage.currentSrc.endsWith("/match.png"), "Media query '" + testImage.title + "' in picture should match."); + } +}; + +// __pushPref(key, value)__. +// Set a pref value asynchronously, returning a promise that resolves +// when it succeeds. +let pushPref = function (key, value) { + return new Promise(function(resolve, reject) { + SpecialPowers.pushPrefEnv({"set": [[key, value]]}, resolve); + }); +}; // __test(isContent)__. -// Run all tests. -let test = function(isContent) { - let {value: prefValue, done} = prefVals.next(); - if (done) { - SimpleTest.finish(); - return; +// Run all tests. A generator function to be used +// with SpawnTask.js. +let test = function* (isContent) { + for (prefValue of [false, true]) { + yield pushPref("privacy.resistFingerprinting", prefValue); + let resisting = prefValue && isContent; + expected_values.forEach( + function ([key, offVal, onVal]) { + testMatch(key, resisting ? onVal : offVal); + }); + testToggles(resisting); + if (OS === "WINNT") { + testWindowsSpecific(resisting, "-moz-os-version", windows_versions); + testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes); + } + testCSS(resisting); + if (OS === "Darwin") { + testOSXFontSmoothing(resisting); + } + yield testMediaQueriesInPictureElements(resisting); } - SpecialPowers.pushPrefEnv({set: [["privacy.resistFingerprinting", prefValue]]}, - function () { - let resisting = prefValue && isContent; - expected_values.forEach( - function ([key, offVal, onVal]) { - testMatch(key, resisting ? onVal : offVal); - }); - testToggles(resisting); - if (OS === "WINNT") { - testWindowsSpecific(resisting, "-moz-os-version", windows_versions); - testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes); - } - testCSS(resisting); - if (OS === "Darwin") { - testOSXFontSmoothing(resisting); - } - test(isContent); - }); }; diff --git a/layout/style/test/chrome/chrome.ini b/layout/style/test/chrome/chrome.ini index e84b98339216..6064cce12b28 100644 --- a/layout/style/test/chrome/chrome.ini +++ b/layout/style/test/chrome/chrome.ini @@ -6,6 +6,8 @@ support-files = bug535806-html.html bug535806-xul.xul hover_helper.html + match.png + mismatch.png [test_addSheet.html] [test_additional_sheets.html] diff --git a/layout/style/test/chrome/match.png b/layout/style/test/chrome/match.png new file mode 100644 index 0000000000000000000000000000000000000000..d3f299bf58248c86158c94facf3bdb305cbdd795 GIT binary patch literal 1210 zcmV;r1V#IaP)f9RHj&B8d_NLg>775@Cx$^+ZY49rEN^mzro=Z<`uRv26#)`n4o-Uv9k1` zSB@fF7>@IO=kZ*1%w6m$6F)_iUli0mYQ%lCW=5kpJ?I2^Oflrg3o$Y^GHvatt!ii| zwOF}3`ad%sE5L2J?GP^r!i0VTVdWBB9=t>sApkBFcEM$~3x2=x8~jWBW?Zl(vlU@J z;Rq=Y!i9lf{;`(y>@M!Z(C8o@SNK{7@b_6NNQDtFnM_bL6`{0AK^IAXatkWrcVU)b z7A{`zglNZ-85dM#pMq$C7y%`V(099cTEJcM9`tLk(te9Q0;eU14I8W!{xRn?LcBvM z;4>OEA}f~BSxvHLMEb6P&Y+_U2A29GRvd?l*h4gYm9f)_?=% zEvBk?JNr;u4`}19H zcPQ0$C_1qn&98lng|5D6zW6n|Z+2s|Gznu2F~&PaIL~&by;WT*tPj~hUG&%M#1=Vg zz=0l17?x|OO8Jf3=wdtLcA=m#54q8A;KtYxRe0}9wYW8Y6S+rokQ9-OZO`Px-Dw_O zwsupU#nK{A5l9%G?QOR>=EDsiL+B!e#5)9&`bl`b=|OQFPWb|>0%NdtZ!C^=G_u7? z>sbR{$}G72T`7LbX=7M#h0dr$)S{=bP%cDAekTnX0=R132iU}@YHw$6Sk}-s#2w@g4~|}^{m4UzB}8W*$wbu;ySW2Q!lp6pIDB3IOyhRV z89ZOT+Opu*BiR&0|L|2*w(LaZ$qJDBPuxKiHUlPoA|8uCed2zcy!;(jRjs7`%GQ+Q z#o%?A^N_P86UcdG{Ch}a0y6VZ2C7wixB)Ek6eGAygp}o}$coCL0Y(_FZ>yt#iNXcP z@+gRo{-2TF@G4$r?qtfcRJ!2$nio*;Y$2YQzm%#&MZN*NM@oQApnjg?3_?0@mE>f#fYQ$V)@8mxO{Or1TB< z!fTEK@j+{0C$PhTa}8`q-E;l_3izJ4;INPf%puuU8O1AX z71}u*$}7wv*;X0FD{K|oIULF>%puuU8O1AX71}u*$}7wv*;W~Ke=5A=t5U15ZH=LS Y0NTcg$=Xm)tpET307*qoM6N<$g6-QvZ2$lO literal 0 HcmV?d00001 diff --git a/layout/style/test/chrome/mismatch.png b/layout/style/test/chrome/mismatch.png new file mode 100644 index 0000000000000000000000000000000000000000..8f9da3f00ff947f14075b37bac24269a27d6b202 GIT binary patch literal 1573 zcmV+=2HN?FP)+!rN)jtJVnJ{)q9lSb27&?6i4DYp(MYkENemVg%jnbC zP)7%q7!?eovBVk`1OyAcBe}cIV~|MXy^~=klXL&Om$PTj{q48kUO*>iO>lY4+F-~_ zI|4cqpmyNYQ4*jSKS+QQpdbOkaH&8EP>_INxKyA7C`dpsTq;lk6eJ)RE)^&N3K9?u zmkN{s1qld-O9j>@K(CF5U}kNPCpQzZdFqHaQhP*1!@_fhfw$sWu=$bp?evSy=Be=-r#vaD_$+oOLJ>Z93baq_6-h zN82Fd;T=vxFAx;_5Dl6%!}H`nv2v6(mR?Lp?#pLb^l`fy9!2|Q-|V>nQ>)%eUMr;C zyZP3Q=!}amq$2l429^x$pnb9`ks1N?+xsibZ2F+Qyc{Q#zha+Qx0ttcaQB8w;9BVD z>LUHYANX!~XMS$d(F!TIul;L>t!^X#{g=_ElTx(7=EzJ-t`=bM`DA3Lr||h&S#+ru zwk8MG2;j^E@o3kr7XwWCJsuIR_MFRm`)QcleT>rLVm@H#SX<0Kd=&**FA?V46HWok z(El?()U8*a*C$<1z>m{MV@7lw>NjqRI{NjXU%vsj*R@kB^cph>x_Wg`n469Li+qr9 zC<@lYT;LqI22G4jc#qQJBK&qF8pQ>9_|V;lpS>z8Mf990xOXWQUu``FB zcHBw4NO=T%7f%NLs;rFj(ODl{i9^p(6QQG{`-UB9N>XEhtt`4KRr`snek61biu3a@ zE@&-|gw4g^FTO>d;(G*;+JzuQ&7KV3UFQ^Y&&AEyqqueMI7~k1j@uVb!E?Qel@!>= zxWY&21?Jls@jXE!9}O8|fr;O5Lw@E999-s)KI6UM=)C}qj9T)`PYR2wXkinjm1)=| z-?&oIA)hXfM6)(#+&v{lg`AJBshfW0B5m;=iyLPSYs!w-YYdQlEqt6EW~vhKAmKd9 zl(x0(I|#wbu$X`H4!28XfFX{Sa0~f?6{A;IpOcb;&PGO{{}g|QOlufeC67|ytAAc~ z586jP%qEBLWWQbE)EzC_TQV4G@2FXmDxg%|kA6UfCn2bo%)t4mb+reO%=*hMF=%dP z!AZ%}HhYbzP+et!+x*;EnWS23GwWflT=lG{PuTye0({%OD#VYWWGVhi;A11hL4Hi# zKnhd?q;s|J(H9;OyK(p88EpLAkx{pT9Th$2Q z(eQZ;CH3xg?3p(e_O71j>-O6CBYj;l+;<^bE4DzYqe6LvN5i053&ibQkId909v-Eo zCCZd3N6xdfzX7aO1*)rFQkaiOihPw7NRyiekT#b_9;i(MbqoQrRoaLqI1LhQu8t0lFf=hm z;<3H|_#20MZ<`V0m2Xo$Bp%(ZO}ZMz)EdAVo#sEdNCFfrngl2T3K9?umkN{s1qld- zO9e`Rf&>J^r2-{DK>~u|Qh^enAOXQ}sXz%(kbq#gRG06?k&PQo(Khzu|uY XKlOUk;hZpA00000NkvXXu0mjfI)mQa literal 0 HcmV?d00001 diff --git a/layout/style/test/chrome/test_bug418986-2.xul b/layout/style/test/chrome/test_bug418986-2.xul index 8424e15ac108..2e5f8e6879e2 100644 --- a/layout/style/test/chrome/test_bug418986-2.xul +++ b/layout/style/test/chrome/test_bug418986-2.xul @@ -7,13 +7,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 @@ -21,7 +22,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 97a0b067cb53..15fec346d291 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -6,6 +6,8 @@ support-files = ccd-standards.html css_properties.js chrome/bug418986-2.js + chrome/match.png + chrome/mismatch.png descriptor_database.js empty.html media_queries_dynamic_xbl_binding.xml diff --git a/layout/style/test/test_bug418986-2.html b/layout/style/test/test_bug418986-2.html index 15d7c71ce44e..aa9ff8af1f29 100644 --- a/layout/style/test/test_bug418986-2.html +++ b/layout/style/test/test_bug418986-2.html @@ -7,13 +7,16 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 Test 2/3 for Bug #418986: Resist fingerprinting by preventing exposure of screen and system info + @@ -23,6 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418986 +

 
From 7fd213b1651910cdf1dd1b5c1ce09d53c5298cd8 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Mon, 24 Aug 2015 11:43:00 -0400 Subject: [PATCH 038/101] Bug 1197930 - Add comments for getRuleLine and getRuleColumn. r=heycam IGNORE IDL --- layout/inspector/inIDOMUtils.idl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/layout/inspector/inIDOMUtils.idl b/layout/inspector/inIDOMUtils.idl index 1488f219b716..daaff3a98eb3 100644 --- a/layout/inspector/inIDOMUtils.idl +++ b/layout/inspector/inIDOMUtils.idl @@ -25,7 +25,21 @@ interface inIDOMUtils : nsISupports [optional] out unsigned long aLength, [array, size_is (aLength), retval] out nsISupports aSheets); nsISupportsArray getCSSStyleRules(in nsIDOMElement aElement, [optional] in DOMString aPseudo); + + /** + * Get the line number of a rule. + * + * @param nsIDOMCSSRule aRule The rule. + * @return The rule's line number. Line numbers are 1-based. + */ unsigned long getRuleLine(in nsIDOMCSSRule aRule); + + /** + * Get the column number of a rule. + * + * @param nsIDOMCSSRule aRule The rule. + * @return The rule's column number. Column numbers are 1-based. + */ unsigned long getRuleColumn(in nsIDOMCSSRule aRule); /** From 2816d774f0b738421fea84d9a3886dee1b4d2a91 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 25 Aug 2015 05:42:50 +0900 Subject: [PATCH 039/101] Bug 1192412 - Part 0: Refactor property list parsing. r=efaust --HG-- extra : source : 6483795d4c85d050f9cb0329a3cac6b85d374490 extra : amend_source : 79f11d02dcd5318af67ab67b7abcefc728cd3437 --- js/src/frontend/Parser.cpp | 709 ++++++++++++++++++++----------------- js/src/frontend/Parser.h | 26 +- 2 files changed, 398 insertions(+), 337 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index b9baf8af705f..5ed7bbb11deb 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6208,6 +6208,52 @@ Parser::debuggerStatement() return handler.newDebuggerStatement(p); } +static JSOp +JSOpFromPropertyType(PropertyType propType) +{ + switch (propType) { + case PropertyType::Getter: + return JSOP_INITPROP_GETTER; + case PropertyType::Setter: + return JSOP_INITPROP_SETTER; + case PropertyType::Normal: + case PropertyType::Method: + case PropertyType::GeneratorMethod: + case PropertyType::Constructor: + case PropertyType::DerivedConstructor: + return JSOP_INITPROP; + default: + MOZ_CRASH("unexpected property type"); + } +} + +static FunctionSyntaxKind +FunctionSyntaxKindFromPropertyType(PropertyType propType) +{ + switch (propType) { + case PropertyType::Getter: + return Getter; + case PropertyType::Setter: + return Setter; + case PropertyType::Method: + return Method; + case PropertyType::GeneratorMethod: + return Method; + case PropertyType::Constructor: + return ClassConstructor; + case PropertyType::DerivedConstructor: + return DerivedClassConstructor; + default: + MOZ_CRASH("unexpected property type"); + } +} + +static GeneratorKind +GeneratorKindFromPropertyType(PropertyType propType) +{ + return propType == PropertyType::GeneratorMethod ? StarGenerator : NotGenerator; +} + template <> ParseNode* Parser::classDefinition(YieldHandling yieldHandling, @@ -6251,6 +6297,8 @@ Parser::classDefinition(YieldHandling yieldHandling, ParseNode* classBlock = null(); + RootedAtom propAtom(context); + // A named class creates a new lexical scope with a const binding of the // class name. Maybe classStmt; @@ -6280,10 +6328,90 @@ Parser::classDefinition(YieldHandling yieldHandling, MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS); - ParseNode* classMethods = propertyList(yieldHandling, - hasHeritage ? DerivedClassBody : ClassBody); - if (!classMethods) + ParseNode* classMethods = handler.newClassMethodList(pos().begin); + + bool seenConstructor = false; + for (;;) { + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + return null(); + if (tt == TOK_RC) + break; + + if (tt == TOK_SEMI) + continue; + + bool isStatic = false; + if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) { + if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName)) + return null(); + if (tt != TOK_LP) { + isStatic = true; + } else { + tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); + tokenStream.ungetToken(); + } + } else { + tokenStream.ungetToken(); + } + + PropertyType propType; + ParseNode* propName = propertyName(yieldHandling, classMethods, &propType, &propAtom); + if (!propName) + return null(); + + if (propType != PropertyType::Getter && propType != PropertyType::Setter && + propType != PropertyType::Method && propType != PropertyType::GeneratorMethod && + propType != PropertyType::Constructor && propType != PropertyType::DerivedConstructor) + { + report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); + return null(); + } + + if (!isStatic && propAtom == context->names().constructor) { + if (propType != PropertyType::Method) { + report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF); + return null(); + } + if (seenConstructor) { + report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "constructor"); + return null(); + } + seenConstructor = true; + propType = hasHeritage ? PropertyType::DerivedConstructor : PropertyType::Constructor; + } else if (isStatic && propAtom == context->names().prototype) { + report(ParseError, false, propName, JSMSG_BAD_METHOD_DEF); + return null(); + } + + // FIXME: Implement ES6 function "name" property semantics + // (bug 883377). + RootedPropertyName funName(context); + switch (propType) { + case PropertyType::Getter: + case PropertyType::Setter: + funName = nullptr; + break; + default: + if (tokenStream.isCurrentTokenType(TOK_NAME)) + funName = tokenStream.currentName(); + else + funName = nullptr; + } + ParseNode* fn = methodDefinition(yieldHandling, propType, funName); + if (!fn) + return null(); + + JSOp op = JSOpFromPropertyType(propType); + if (!handler.addClassMethodDefinition(classMethods, propName, fn, op, isStatic)) + return null(); + } + + // Default constructors not yet implemented. See bug 1105463 + if (!seenConstructor) { + report(ParseError, false, null(), JSMSG_NO_CLASS_CONSTRUCTOR); return null(); + } ParseNode* nameNode = null(); ParseNode* methodsOrBlock = classMethods; @@ -8523,6 +8651,151 @@ DoubleToAtom(ExclusiveContext* cx, double value) return ToAtom(cx, HandleValue::fromMarkedLocation(&tmp)); } +template +typename ParseHandler::Node +Parser::propertyName(YieldHandling yieldHandling, Node propList, + PropertyType* propType, MutableHandleAtom propAtom) +{ + TokenKind ltok; + if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) + return null(); + + // TOK_RC should be handled in caller. + MOZ_ASSERT(ltok != TOK_RC); + + bool isGenerator = false; + if (ltok == TOK_MUL) { + isGenerator = true; + if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) + return null(); + } + + propAtom.set(nullptr); + Node propName; + switch (ltok) { + case TOK_NUMBER: + propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number())); + if (!propAtom.get()) + return null(); + propName = newNumber(tokenStream.currentToken()); + if (!propName) + return null(); + break; + + case TOK_LB: + propName = computedPropertyName(yieldHandling, propList); + if (!propName) + return null(); + break; + + case TOK_NAME: { + propAtom.set(tokenStream.currentName()); + // Do not look for accessor syntax on generators + if (isGenerator || + !(propAtom.get() == context->names().get || + propAtom.get() == context->names().set)) + { + propName = handler.newObjectLiteralPropertyName(propAtom, pos()); + if (!propName) + return null(); + break; + } + + *propType = propAtom.get() == context->names().get ? PropertyType::Getter + : PropertyType::Setter; + + // We have parsed |get| or |set|. Look for an accessor property + // name next. + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + return null(); + if (tt == TOK_NAME) { + propAtom.set(tokenStream.currentName()); + return handler.newObjectLiteralPropertyName(propAtom, pos()); + } + if (tt == TOK_STRING) { + propAtom.set(tokenStream.currentToken().atom()); + + uint32_t index; + if (propAtom->isIndex(&index)) { + propAtom.set(DoubleToAtom(context, index)); + if (!propAtom.get()) + return null(); + return handler.newNumber(index, NoDecimal, pos()); + } + return stringLiteral(); + } + if (tt == TOK_NUMBER) { + propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number())); + if (!propAtom.get()) + return null(); + return newNumber(tokenStream.currentToken()); + } + if (tt == TOK_LB) + return computedPropertyName(yieldHandling, propList); + + // Not an accessor property after all. + tokenStream.ungetToken(); + propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos()); + if (!propName) + return null(); + tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); + break; + } + + case TOK_STRING: { + propAtom.set(tokenStream.currentToken().atom()); + uint32_t index; + if (propAtom->isIndex(&index)) { + propName = handler.newNumber(index, NoDecimal, pos()); + if (!propName) + return null(); + break; + } + propName = stringLiteral(); + if (!propName) + return null(); + break; + } + + default: + report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + return null(); + } + + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + + if (tt == TOK_COLON) { + if (isGenerator) { + report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + return null(); + } + *propType = PropertyType::Normal; + return propName; + } + + if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) { + if (isGenerator) { + report(ParseError, false, null(), JSMSG_BAD_PROP_ID); + return null(); + } + tokenStream.ungetToken(); + *propType = PropertyType::Shorthand; + return propName; + } + + if (tt == TOK_LP) { + tokenStream.ungetToken(); + *propType = isGenerator ? PropertyType::GeneratorMethod : PropertyType::Method; + return propName; + } + + report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); + return null(); +} + template typename ParseHandler::Node Parser::computedPropertyName(YieldHandling yieldHandling, Node literal) @@ -8550,335 +8823,119 @@ Parser::computedPropertyName(YieldHandling yieldHandling, Node lit template typename ParseHandler::Node -Parser::newPropertyListNode(PropListType type) +Parser::objectLiteral(YieldHandling yieldHandling) { - if (IsClassBody(type)) - return handler.newClassMethodList(pos().begin); + MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); - MOZ_ASSERT(type == ObjectLiteral); - return handler.newObjectLiteral(pos().begin); + Node literal = handler.newObjectLiteral(pos().begin); + if (!literal) + return null(); + + bool seenPrototypeMutation = false; + RootedAtom propAtom(context); + for (;;) { + TokenKind tt; + if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) + return null(); + if (tt == TOK_RC) + break; + + tokenStream.ungetToken(); + + PropertyType propType; + Node propName = propertyName(yieldHandling, literal, &propType, &propAtom); + if (!propName) + return null(); + + if (propType == PropertyType::Normal) { + Node propExpr = assignExpr(InAllowed, yieldHandling); + if (!propExpr) + return null(); + + if (foldConstants && !FoldConstants(context, &propExpr, this)) + return null(); + + if (propAtom == context->names().proto) { + if (seenPrototypeMutation) { + report(ParseError, false, propName, JSMSG_DUPLICATE_PROPERTY, "__proto__"); + return null(); + } + seenPrototypeMutation = true; + + // Note: this occurs *only* if we observe TOK_COLON! Only + // __proto__: v mutates [[Prototype]]. Getters, setters, + // method/generator definitions, computed property name + // versions of all of these, and shorthands do not. + uint32_t begin = handler.getPosition(propName).begin; + if (!handler.addPrototypeMutation(literal, begin, propExpr)) + return null(); + } else { + if (!handler.isConstant(propExpr)) + handler.setListFlag(literal, PNX_NONCONST); + + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } + } else if (propType == PropertyType::Shorthand) { + /* + * Support, e.g., |var {x, y} = o| as destructuring shorthand + * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8. + */ + if (!tokenStream.checkForKeyword(propAtom, nullptr)) + return null(); + + Node nameExpr = identifierName(yieldHandling); + if (!nameExpr) + return null(); + + if (!handler.addShorthand(literal, propName, nameExpr)) + return null(); + } else { + // FIXME: Implement ES6 function "name" property semantics + // (bug 883377). + RootedPropertyName funName(context); + switch (propType) { + case PropertyType::Getter: + case PropertyType::Setter: + funName = nullptr; + break; + default: + if (tokenStream.isCurrentTokenType(TOK_NAME)) + funName = tokenStream.currentName(); + else + funName = nullptr; + } + Node fn = methodDefinition(yieldHandling, propType, funName); + if (!fn) + return null(); + + JSOp op = JSOpFromPropertyType(propType); + if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) + return null(); + } + + if (!tokenStream.getToken(&tt)) + return null(); + if (tt == TOK_RC) + break; + if (tt != TOK_COMMA) { + report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST); + return null(); + } + } + + handler.setEndPosition(literal, pos().end); + return literal; } template typename ParseHandler::Node -Parser::propertyList(YieldHandling yieldHandling, PropListType type) +Parser::methodDefinition(YieldHandling yieldHandling, PropertyType propType, + HandlePropertyName funName) { - MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC)); - - Node propList = newPropertyListNode(type); - if (!propList) - return null(); - - bool seenPrototypeMutation = false; - bool seenConstructor = false; - RootedAtom atom(context); - for (;;) { - TokenKind ltok; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) - return null(); - if (ltok == TOK_RC) - break; - - bool isStatic = false; - if (IsClassBody(type)) { - if (ltok == TOK_SEMI) - continue; - - if (ltok == TOK_NAME && - tokenStream.currentName() == context->names().static_) - { - isStatic = true; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) - return null(); - } - } - - bool isGenerator = false; - if (ltok == TOK_MUL) { - isGenerator = true; - if (!tokenStream.getToken(<ok, TokenStream::KeywordIsName)) - return null(); - } - - atom = nullptr; - - JSOp op = JSOP_INITPROP; - Node propname; - bool isConstructor = false; - switch (ltok) { - case TOK_NUMBER: - atom = DoubleToAtom(context, tokenStream.currentToken().number()); - if (!atom) - return null(); - propname = newNumber(tokenStream.currentToken()); - if (!propname) - return null(); - break; - - case TOK_LB: { - propname = computedPropertyName(yieldHandling, propList); - if (!propname) - return null(); - break; - } - - case TOK_NAME: { - atom = tokenStream.currentName(); - // Do not look for accessor syntax on generators - if (!isGenerator && - (atom == context->names().get || - atom == context->names().set)) - { - op = atom == context->names().get ? JSOP_INITPROP_GETTER - : JSOP_INITPROP_SETTER; - } else { - propname = handler.newObjectLiteralPropertyName(atom, pos()); - if (!propname) - return null(); - break; - } - - // We have parsed |get| or |set|. Look for an accessor property - // name next. - TokenKind tt; - if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName)) - return null(); - if (tt == TOK_NAME) { - atom = tokenStream.currentName(); - propname = handler.newObjectLiteralPropertyName(atom, pos()); - if (!propname) - return null(); - } else if (tt == TOK_STRING) { - atom = tokenStream.currentToken().atom(); - - uint32_t index; - if (atom->isIndex(&index)) { - propname = handler.newNumber(index, NoDecimal, pos()); - if (!propname) - return null(); - atom = DoubleToAtom(context, index); - if (!atom) - return null(); - } else { - propname = stringLiteral(); - if (!propname) - return null(); - } - } else if (tt == TOK_NUMBER) { - atom = DoubleToAtom(context, tokenStream.currentToken().number()); - if (!atom) - return null(); - propname = newNumber(tokenStream.currentToken()); - if (!propname) - return null(); - } else if (tt == TOK_LB) { - propname = computedPropertyName(yieldHandling, propList); - if (!propname) - return null(); - } else { - // Not an accessor property after all. - tokenStream.ungetToken(); - propname = handler.newObjectLiteralPropertyName(atom, pos()); - if (!propname) - return null(); - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); - op = JSOP_INITPROP; - break; - } - - MOZ_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER); - break; - } - - case TOK_STRING: { - atom = tokenStream.currentToken().atom(); - uint32_t index; - if (atom->isIndex(&index)) { - propname = handler.newNumber(index, NoDecimal, pos()); - if (!propname) - return null(); - } else { - propname = stringLiteral(); - if (!propname) - return null(); - } - break; - } - - default: - // There is never a case in which |static *(| can make a meaningful method definition. - if (isStatic && !isGenerator) { - // Turns out it wasn't static. Put it back and pretend it was a name all along. - tokenStream.ungetToken(); - if (isStatic) - tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName); - isStatic = false; - atom = tokenStream.currentName(); - propname = handler.newObjectLiteralPropertyName(atom->asPropertyName(), pos()); - if (!propname) - return null(); - } else { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); - return null(); - } - } - - if (IsClassBody(type)) { - if (!isStatic && atom == context->names().constructor) { - if (isGenerator || op != JSOP_INITPROP) { - report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF); - return null(); - } - if (seenConstructor) { - report(ParseError, false, propname, JSMSG_DUPLICATE_PROPERTY, "constructor"); - return null(); - } - seenConstructor = true; - isConstructor = true; - } else if (isStatic && atom == context->names().prototype) { - report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF); - return null(); - } - } - - if (op == JSOP_INITPROP) { - TokenKind tt; - if (!tokenStream.getToken(&tt)) - return null(); - - if (tt == TOK_COLON) { - if (IsClassBody(type)) { - report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); - return null(); - } - if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); - return null(); - } - - Node propexpr = assignExpr(InAllowed, yieldHandling); - if (!propexpr) - return null(); - - if (foldConstants && !FoldConstants(context, &propexpr, this)) - return null(); - - if (atom == context->names().proto) { - if (seenPrototypeMutation) { - report(ParseError, false, propname, JSMSG_DUPLICATE_PROPERTY, "__proto__"); - return null(); - } - seenPrototypeMutation = true; - - // Note: this occurs *only* if we observe TOK_COLON! Only - // __proto__: v mutates [[Prototype]]. Getters, setters, - // method/generator definitions, computed property name - // versions of all of these, and shorthands do not. - uint32_t begin = handler.getPosition(propname).begin; - if (!handler.addPrototypeMutation(propList, begin, propexpr)) - return null(); - } else { - if (!handler.isConstant(propexpr)) - handler.setListFlag(propList, PNX_NONCONST); - - if (!handler.addPropertyDefinition(propList, propname, propexpr)) - return null(); - } - } else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) { - /* - * Support, e.g., |var {x, y} = o| as destructuring shorthand - * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8. - */ - if (IsClassBody(type)) { - report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF); - return null(); - } - if (isGenerator) { - report(ParseError, false, null(), JSMSG_BAD_PROP_ID); - return null(); - } - - tokenStream.ungetToken(); - if (!tokenStream.checkForKeyword(atom, nullptr)) - return null(); - - Node nameExpr = identifierName(yieldHandling); - if (!nameExpr) - return null(); - - if (!handler.addShorthand(propList, propname, nameExpr)) - return null(); - } else if (tt == TOK_LP) { - tokenStream.ungetToken(); - if (!methodDefinition(yieldHandling, type, propList, propname, - isConstructor ? type == DerivedClassBody ? DerivedClassConstructor - : ClassConstructor - : Method, - isGenerator ? StarGenerator : NotGenerator, isStatic, op)) - { - return null(); - } - } else { - report(ParseError, false, null(), JSMSG_COLON_AFTER_ID); - return null(); - } - } else { - if (!methodDefinition(yieldHandling, type, propList, propname, - op == JSOP_INITPROP_GETTER ? Getter : Setter, NotGenerator, - isStatic, op)) - { - return null(); - } - } - - if (type == ObjectLiteral) { - TokenKind tt; - if (!tokenStream.getToken(&tt)) - return null(); - if (tt == TOK_RC) - break; - if (tt != TOK_COMMA) { - report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST); - return null(); - } - } - } - - // Default constructors not yet implemented. See bug 1105463 - if (IsClassBody(type) && !seenConstructor) { - report(ParseError, false, null(), JSMSG_NO_CLASS_CONSTRUCTOR); - return null(); - } - - handler.setEndPosition(propList, pos().end); - return propList; -} - -template -bool -Parser::methodDefinition(YieldHandling yieldHandling, PropListType listType, - Node propList, Node propname, FunctionSyntaxKind kind, - GeneratorKind generatorKind, bool isStatic, JSOp op) -{ - MOZ_ASSERT(kind == Method || kind == ClassConstructor || kind == DerivedClassConstructor || - kind == Getter || kind == Setter); - /* NB: Getter function in { get x(){} } is unnamed. */ - RootedPropertyName funName(context); - if ((kind == Method || IsConstructorKind(kind)) && tokenStream.isCurrentTokenType(TOK_NAME)) { - funName = tokenStream.currentName(); - } else { - funName = nullptr; - } - - Node fn = functionDef(InAllowed, yieldHandling, funName, kind, generatorKind); - if (!fn) - return false; - - if (IsClassBody(listType)) - return handler.addClassMethodDefinition(propList, propname, fn, op, isStatic); - - MOZ_ASSERT(listType == ObjectLiteral); - return handler.addObjectMethodDefinition(propList, propname, fn, op); + FunctionSyntaxKind kind = FunctionSyntaxKindFromPropertyType(propType); + GeneratorKind generatorKind = GeneratorKindFromPropertyType(propType); + return functionDef(InAllowed, yieldHandling, funName, kind, generatorKind); } template @@ -8936,7 +8993,7 @@ Parser::primaryExpr(YieldHandling yieldHandling, TokenKind tt, return arrayInitializer(yieldHandling); case TOK_LC: - return propertyList(yieldHandling, ObjectLiteral); + return objectLiteral(yieldHandling); case TOK_LP: { TokenKind next; diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index b2c2457577fc..45cc79fa2605 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -355,12 +355,16 @@ class CompExprTransplanter; enum VarContext { HoistVars, DontHoistVars }; enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody }; - -inline bool -IsClassBody(PropListType type) -{ - return type == ClassBody || type == DerivedClassBody; -} +enum class PropertyType { + Normal, + Shorthand, + Getter, + Setter, + Method, + GeneratorMethod, + Constructor, + DerivedConstructor +}; // Specify a value for an ES6 grammar parametrization. We have no enum for // [Return] because its behavior is exactly equivalent to checking whether @@ -691,9 +695,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool tryNewTarget(Node& newTarget); bool checkAndMarkSuperScope(); - bool methodDefinition(YieldHandling yieldHandling, PropListType listType, Node propList, - Node propname, FunctionSyntaxKind kind, GeneratorKind generatorKind, - bool isStatic, JSOp Op); + Node methodDefinition(YieldHandling yieldHandling, PropertyType propType, + HandlePropertyName funName); /* * Additional JS parsers. @@ -798,12 +801,13 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter Node pushLexicalScope(Handle blockObj, AutoPushStmtInfoPC& stmt); Node pushLetScope(Handle blockObj, AutoPushStmtInfoPC& stmt); bool noteNameUse(HandlePropertyName name, Node pn); + Node propertyName(YieldHandling yieldHandling, Node propList, + PropertyType* propType, MutableHandleAtom propAtom); Node computedPropertyName(YieldHandling yieldHandling, Node literal); Node arrayInitializer(YieldHandling yieldHandling); Node newRegExp(); - Node propertyList(YieldHandling yieldHandling, PropListType type); - Node newPropertyListNode(PropListType type); + Node objectLiteral(YieldHandling yieldHandling); bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos); Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos); From df599b65c04ad2688ffd5274a2bac0c4b2539567 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Tue, 11 Aug 2015 08:39:01 +0900 Subject: [PATCH 040/101] Bug 1192412 - Part 1: Set class constructor name to class name. r=efaust --HG-- extra : rebase_source : 652f849bf960c5038a088e54a2fa72613783ef6a extra : amend_source : 23aa57602920dc9c44492ce6cbc6d53e0f586c32 --- js/src/frontend/Parser.cpp | 4 + js/src/tests/ecma_6/Class/className.js | 246 ++++++++++++++++++ js/src/tests/js1_8_5/reflect-parse/classes.js | 77 +++--- 3 files changed, 294 insertions(+), 33 deletions(-) create mode 100644 js/src/tests/ecma_6/Class/className.js diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 5ed7bbb11deb..f8711d381ddd 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6392,6 +6392,10 @@ Parser::classDefinition(YieldHandling yieldHandling, case PropertyType::Setter: funName = nullptr; break; + case PropertyType::Constructor: + case PropertyType::DerivedConstructor: + funName = name; + break; default: if (tokenStream.isCurrentTokenType(TOK_NAME)) funName = tokenStream.currentName(); diff --git a/js/src/tests/ecma_6/Class/className.js b/js/src/tests/ecma_6/Class/className.js new file mode 100644 index 000000000000..6d47f790c243 --- /dev/null +++ b/js/src/tests/ecma_6/Class/className.js @@ -0,0 +1,246 @@ +function testName(C, name, hasValue, hasGetter, hasSetter, isFunction=false) { + if (isFunction) { + assertEq(typeof C.name, "function"); + } else { + assertEq(C.name, name); + } + + var desc = Object.getOwnPropertyDescriptor(C, "name"); + if (!hasValue && !hasGetter && !hasSetter) { + assertEq(desc, undefined); + return; + } + + if (hasValue) { + assertEq("value" in desc, true); + if (isFunction) { + assertEq(typeof desc.value, "function"); + } else { + assertEq(desc.value, name); + } + // FIXME: Methods defined in classes should not be enumerable + // (bug 1144630). + // assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + + assertEq("get" in desc, false); + assertEq("set" in desc, false); + + return; + } + + assertEq("value" in desc, false); + + if (hasGetter) { + assertEq("get" in desc, true); + assertEq(desc.get(), name); + // assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + } else { + assertEq("get" in desc, true); + assertEq(desc.get, undefined); + // assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + } + + if (hasSetter) { + assertEq("set" in desc, true); + assertEq(typeof desc.set, "function"); + // assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + } else { + assertEq("set" in desc, true); + assertEq(desc.set, undefined); + // assertEq(desc.enumerable, false); + assertEq(desc.configurable, true); + } +} + +var test = ` +// ---- declaration --- + +class Decl { + constructor() {} +} +testName(Decl, "Decl", true, false, false); + +class DeclWithFunc { + constructor() {} + static name() {} +} +testName(DeclWithFunc, "DeclWithFunc", true, false, false, true); + +class DeclWithGetter { + constructor() {} + static get name() { return "base"; } +} +testName(DeclWithGetter, "base", false, true, false); + +class DeclWithSetter { + constructor() {} + static set name(v) {} +} +testName(DeclWithSetter, undefined, false, false, true); + +class DeclWithGetterSetter { + constructor() {} + static get name() { return "base"; } + static set name(v) {} +} +testName(DeclWithGetterSetter, "base", false, true, true); + +function prop() { + return "name"; +} +class DeclWithComputed { + constructor() {} + static get [prop()]() { return "base"; } +} +testName(DeclWithGetterSetter, "base", false, true, true); + +class ExtendedDecl1 extends Decl { + constructor() {} +} +testName(ExtendedDecl1, "ExtendedDecl1", true, false, false); +delete ExtendedDecl1.name; +testName(ExtendedDecl1, "Decl", false, false, false); + +class ExtendedDecl2 extends DeclWithGetterSetter { + constructor() {} + static get name() { return "extend"; } +} +testName(ExtendedDecl2, "extend", false, true, false); +delete ExtendedDecl2.name; +testName(ExtendedDecl2, "base", false, false, false); + +class ExtendedDecl3 extends DeclWithGetterSetter { + constructor() {} + static set name(v) {} +} +testName(ExtendedDecl3, undefined, false, false, true); +delete ExtendedDecl3.name; +testName(ExtendedDecl3, "base", false, false, false); + +// ---- expression --- + +let Expr = class Expr { + constructor() {} +}; +testName(Expr, "Expr", true, false, false); + +let ExprWithGetter = class ExprWithGetter { + constructor() {} + static get name() { return "base"; } +}; +testName(ExprWithGetter, "base", false, true, false); + +let ExprWithSetter = class ExprWithSetter { + constructor() {} + static set name(v) {} +}; +testName(ExprWithSetter, undefined, false, false, true); + +let ExprWithGetterSetter = class ExprWithGetterSetter { + constructor() {} + static get name() { return "base"; } + static set name(v) {} +}; +testName(ExprWithGetterSetter, "base", false, true, true); + + +let ExtendedExpr1 = class ExtendedExpr1 extends Expr { + constructor() {} +}; +testName(ExtendedExpr1, "ExtendedExpr1", true, false, false); +delete ExtendedExpr1.name; +testName(ExtendedExpr1, "Expr", false, false, false); + +let ExtendedExpr2 = class ExtendedExpr2 extends ExprWithGetterSetter { + constructor() {} + static get name() { return "extend"; } +}; +testName(ExtendedExpr2, "extend", false, true, false); +delete ExtendedExpr2.name; +testName(ExtendedExpr2, "base", false, false, false); + +let ExtendedExpr3 = class ExtendedExpr3 extends ExprWithGetterSetter { + constructor() {} + static set name(v) {} +}; +testName(ExtendedExpr3, undefined, false, false, true); +delete ExtendedExpr3.name; +testName(ExtendedExpr3, "base", false, false, false); + +// ---- anonymous ---- + +let Anon = class { + constructor() {} +}; +testName(Anon, "", true, false, false); + +let AnonWithGetter = class { + constructor() {} + static get name() { return "base"; } +}; +testName(AnonWithGetter, "base", false, true, false); + +let AnonWithSetter = class { + constructor() {} + static set name(v) {} +}; +testName(AnonWithSetter, undefined, false, false, true); + +let AnonWithGetterSetter = class { + constructor() {} + static get name() { return "base"; } + static set name(v) {} +}; +testName(AnonWithGetterSetter, "base", false, true, true); + + +let ExtendedAnon1 = class extends Anon { + constructor() {} +}; +testName(ExtendedAnon1, "", true, false, false); +delete ExtendedAnon1.name; +testName(ExtendedAnon1, "", false, false, false); + +let ExtendedAnon2 = class extends AnonWithGetterSetter { + constructor() {} + static get name() { return "extend"; } +}; +testName(ExtendedAnon2, "extend", false, true, false); +delete ExtendedAnon2.name; +testName(ExtendedAnon2, "base", false, false, false); + +let ExtendedAnon3 = class extends AnonWithGetterSetter { + constructor() {} + static set name(v) {} +}; +testName(ExtendedAnon3, undefined, false, false, true); +delete ExtendedAnon3.name; +testName(ExtendedAnon3, "base", false, false, false); + +// ---- mixed ---- + +let ExtendedExpr4 = class ExtendedExpr4 extends Anon { + constructor() {} +} +testName(ExtendedExpr4, "ExtendedExpr4", true, false, false); +delete ExtendedExpr4.name; +testName(ExtendedExpr4, "", false, false, false); + +let ExtendedExpr5 = class ExtendedExpr5 extends AnonWithGetterSetter { + constructor() {} + static get name() { return "extend"; } +} +testName(ExtendedExpr5, "extend", false, true, false); +delete ExtendedExpr5.name; +testName(ExtendedExpr5, "base", false, false, false); +`; + +if (classesEnabled()) + eval(test); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "OK"); diff --git a/js/src/tests/js1_8_5/reflect-parse/classes.js b/js/src/tests/js1_8_5/reflect-parse/classes.js index 6544ceb2de7d..371819b2d55c 100644 --- a/js/src/tests/js1_8_5/reflect-parse/classes.js +++ b/js/src/tests/js1_8_5/reflect-parse/classes.js @@ -24,6 +24,11 @@ function testClasses() { methodFun(id, kind, generator, args), kind, isStatic); } + function ctorWithName(id) { + return classMethod(ident("constructor"), + methodFun(id, "method", false, []), + "method", false); + } function emptyCPNMethod(id, isStatic) { return classMethod(computedName(lit(id)), funExpr(null, [], blockStmt([])), @@ -34,9 +39,16 @@ function testClasses() { let template = classExpr(name, heritage, methods); assertExpr("(" + str + ")", template); } + // FunctionExpression of constructor has class name as its id. + // FIXME: Implement ES6 function "name" property semantics (bug 883377). + let ctorPlaceholder = {}; function assertClass(str, methods, heritage=null) { let namelessStr = str.replace("NAME", ""); let namedStr = str.replace("NAME", "Foo"); + let namedCtor = ctorWithName("NAME"); + let namelessCtor = ctorWithName(null); + let namelessMethods = methods.map(x => x == ctorPlaceholder ? namedCtor : x); + let namedMethods = methods.map(x => x == ctorPlaceholder ? namedCtor : x); assertClassExpr(namelessStr, methods, heritage); assertClassExpr(namedStr, methods, heritage, ident("Foo")); @@ -52,13 +64,11 @@ function testClasses() { assertError("(" + str.replace("NAME", "") + ")", error); } - let simpleConstructor = simpleMethod("constructor", "method", false); - /* Trivial classes */ // Unnamed class statements are forbidden, but unnamed class expressions are // just fine. assertError("class { constructor() { } }", SyntaxError); - assertClass("class NAME { constructor() { } }", [simpleConstructor]); + assertClass("class NAME { constructor() { } }", [ctorPlaceholder]); // A class name must actually be a name assertNamedClassError("class x.y { constructor() {} }", SyntaxError); @@ -68,13 +78,13 @@ function testClasses() { // Allow methods and accessors assertClass("class NAME { constructor() { } method() { } }", - [simpleConstructor, simpleMethod("method", "method", false)]); + [ctorPlaceholder, simpleMethod("method", "method", false)]); assertClass("class NAME { constructor() { } get method() { } }", - [simpleConstructor, simpleMethod("method", "get", false)]); + [ctorPlaceholder, simpleMethod("method", "get", false)]); assertClass("class NAME { constructor() { } set method(x) { } }", - [simpleConstructor, simpleMethod("method", "set", false, ["x"])]); + [ctorPlaceholder, simpleMethod("method", "set", false, ["x"])]); /* Static */ assertClass(`class NAME { @@ -84,7 +94,7 @@ function testClasses() { static get getter() { }; static set setter(x) { } }`, - [simpleConstructor, + [ctorPlaceholder, simpleMethod("method", "method", false, [], true), simpleMethod("methodGen", "method", true, [], true), simpleMethod("getter", "get", false, [], true), @@ -92,13 +102,13 @@ function testClasses() { // It's not an error to have a method named static, static, or not. assertClass("class NAME { constructor() { } static() { } }", - [simpleConstructor, simpleMethod("static", "method", false)]); + [ctorPlaceholder, simpleMethod("static", "method", false)]); assertClass("class NAME { static static() { }; constructor() { } }", - [simpleMethod("static", "method", false, [], true), simpleConstructor]); + [simpleMethod("static", "method", false, [], true), ctorPlaceholder]); assertClass("class NAME { static get static() { }; constructor() { } }", - [simpleMethod("static", "get", false, [], true), simpleConstructor]); + [simpleMethod("static", "get", false, [], true), ctorPlaceholder]); assertClass("class NAME { constructor() { }; static set static(x) { } }", - [simpleConstructor, simpleMethod("static", "set", false, ["x"], true)]); + [ctorPlaceholder, simpleMethod("static", "set", false, ["x"], true)]); // You do, however, have to put static in the right spot assertClassError("class NAME { constructor() { }; get static foo() { } }", SyntaxError); @@ -112,7 +122,7 @@ function testClasses() { // You are, however, allowed to have a CPN called prototype as a static assertClass("class NAME { constructor() { }; static [\"prototype\"]() { } }", - [simpleConstructor, emptyCPNMethod("prototype", true)]); + [ctorPlaceholder, emptyCPNMethod("prototype", true)]); /* Constructor */ // Currently, we do not allow default constructors @@ -141,14 +151,14 @@ function testClasses() { let str = "class NAME { constructor() { } " + methods[i][0] + " " + methods[j][0] + " }"; - assertClass(str, [simpleConstructor, methods[i][1], methods[j][1]]); + assertClass(str, [ctorPlaceholder, methods[i][1], methods[j][1]]); } } // It is, however, not an error to have a constructor, and a method with a // computed property name 'constructor' assertClass("class NAME { constructor () { } [\"constructor\"] () { } }", - [simpleConstructor, emptyCPNMethod("constructor", false)]); + [ctorPlaceholder, emptyCPNMethod("constructor", false)]); // It is an error to have a generator or accessor named constructor assertClassError("class NAME { *constructor() { } }", SyntaxError); @@ -157,14 +167,14 @@ function testClasses() { /* Semicolons */ // Allow Semicolons in Class Definitions - assertClass("class NAME { constructor() { }; }", [simpleConstructor]); + assertClass("class NAME { constructor() { }; }", [ctorPlaceholder]); // Allow more than one semicolon, even in otherwise trivial classses - assertClass("class NAME { ;;; constructor() { } }", [simpleConstructor]); + assertClass("class NAME { ;;; constructor() { } }", [ctorPlaceholder]); // Semicolons are optional, even if the methods share a line assertClass("class NAME { method() { } constructor() { } }", - [simpleMethod("method", "method", false), simpleConstructor]); + [simpleMethod("method", "method", false), ctorPlaceholder]); /* Generators */ // No yield as a class name inside a generator @@ -184,7 +194,7 @@ function testClasses() { assertClassError("class NAME { constructor() { } *set foo() { } }", SyntaxError); assertClass("class NAME { *method() { } constructor() { } }", - [simpleMethod("method", "method", true), simpleConstructor]); + [simpleMethod("method", "method", true), ctorPlaceholder]); /* Strictness */ // yield is a strict-mode keyword, and class definitions are always strict. @@ -197,14 +207,15 @@ function testClasses() { /* Bindings */ // Class statements bind lexically, so they should collide with other // in-block lexical bindings, but class expressions don't. + let FooCtor = ctorWithName("Foo"); assertError("{ let Foo; class Foo { constructor() { } } }", TypeError); assertStmt("{ let Foo; (class Foo { constructor() { } }) }", blockStmt([letDecl([{id: ident("Foo"), init: null}]), - exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))])); + exprStmt(classExpr(ident("Foo"), null, [FooCtor]))])); assertError("{ const Foo = 0; class Foo { constructor() { } } }", TypeError); assertStmt("{ const Foo = 0; (class Foo { constructor() { } }) }", blockStmt([constDecl([{id: ident("Foo"), init: lit(0)}]), - exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))])); + exprStmt(classExpr(ident("Foo"), null, [FooCtor]))])); assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", TypeError); assertStmt(`{ (class Foo { @@ -214,16 +225,16 @@ function testClasses() { constructor() { } }); }`, - blockStmt([exprStmt(seqExpr([classExpr(ident("Foo"), null, [simpleConstructor]), - classExpr(ident("Foo"), null, [simpleConstructor])]))])); + blockStmt([exprStmt(seqExpr([classExpr(ident("Foo"), null, [FooCtor]), + classExpr(ident("Foo"), null, [FooCtor])]))])); assertStmt(`{ var x = class Foo { constructor() { } }; class Foo { constructor() { } } }`, blockStmt([varDecl([{ id: ident("x"), - init: classExpr(ident("Foo"), null, [simpleConstructor]) + init: classExpr(ident("Foo"), null, [FooCtor]) }]), - classStmt(ident("Foo"), null, [simpleConstructor])])); + classStmt(ident("Foo"), null, [FooCtor])])); // Can't make a lexical binding without a block. @@ -242,36 +253,36 @@ function testClasses() { // "extends" is still a valid name for a method assertClass("class NAME { constructor() { }; extends() { } }", - [simpleConstructor, simpleMethod("extends", "method", false)]); + [ctorPlaceholder, simpleMethod("extends", "method", false)]); // Immediate expression assertClass("class NAME extends null { constructor() { } }", - [simpleConstructor], lit(null)); + [ctorPlaceholder], lit(null)); // Sequence expresson assertClass("class NAME extends (undefined, undefined) { constructor() { } }", - [simpleConstructor], seqExpr([ident("undefined"), ident("undefined")])); + [ctorPlaceholder], seqExpr([ident("undefined"), ident("undefined")])); // Function expression let emptyFunction = funExpr(null, [], blockStmt([])); assertClass("class NAME extends function(){ } { constructor() { } }", - [simpleConstructor], emptyFunction); + [ctorPlaceholder], emptyFunction); // New expression assertClass("class NAME extends new function(){ }() { constructor() { } }", - [simpleConstructor], newExpr(emptyFunction, [])); + [ctorPlaceholder], newExpr(emptyFunction, [])); // Call expression assertClass("class NAME extends function(){ }() { constructor() { } }", - [simpleConstructor], callExpr(emptyFunction, [])); + [ctorPlaceholder], callExpr(emptyFunction, [])); // Dot expression assertClass("class NAME extends {}.foo { constructor() { } }", - [simpleConstructor], dotExpr(objExpr([]), ident("foo"))); + [ctorPlaceholder], dotExpr(objExpr([]), ident("foo"))); // Member expression assertClass("class NAME extends {}[foo] { constructor() { } }", - [simpleConstructor], memExpr(objExpr([]), ident("foo"))); + [ctorPlaceholder], memExpr(objExpr([]), ident("foo"))); /* SuperProperty */ // NOTE: Some of these tests involve object literals, as SuperProperty is a @@ -367,7 +378,7 @@ function testClasses() { function makeClassSuperPropExpr(fun, type, static) { // We are going right into assertClass, so we don't have to build the // entire statement. - return [simpleConstructor, + return [ctorPlaceholder, classMethod(ident("method"), fun, type, static)]; } function doClassSuperPropAssert(str, expr, extending) { From 3c8e5a28bc1b59f64c0ec580390930cedd745ff8 Mon Sep 17 00:00:00 2001 From: Allison Naaktgeboren Date: Tue, 1 Sep 2015 15:32:33 -0700 Subject: [PATCH 041/101] Bug 1189719 - Recall and display search history within main browser UI.r=mcomella --- mobile/android/base/db/BrowserContract.java | 1 + mobile/android/base/home/BrowserSearch.java | 2 +- mobile/android/base/home/SearchEngineRow.java | 131 ++++++++++++------ .../base/resources/layout/suggestion_item.xml | 9 +- 4 files changed, 98 insertions(+), 45 deletions(-) diff --git a/mobile/android/base/db/BrowserContract.java b/mobile/android/base/db/BrowserContract.java index 11c1bdf0523a..09f133d90bde 100644 --- a/mobile/android/base/db/BrowserContract.java +++ b/mobile/android/base/db/BrowserContract.java @@ -475,6 +475,7 @@ public class BrowserContract { public static final String CONTENT_TYPE = "vnd.android.cursor.dir/searchhistory"; public static final String QUERY = "query"; + public static final String DATE = "date"; public static final String TABLE_NAME = "searchhistory"; public static final Uri CONTENT_URI = Uri.withAppendedPath(SEARCH_HISTORY_AUTHORITY_URI, "searchhistory"); diff --git a/mobile/android/base/home/BrowserSearch.java b/mobile/android/base/home/BrowserSearch.java index 7b898053b869..7ed9fc8990fb 100644 --- a/mobile/android/base/home/BrowserSearch.java +++ b/mobile/android/base/home/BrowserSearch.java @@ -911,7 +911,7 @@ public class BrowserSearch extends HomeFragment final SearchEngine engine = mSearchEngines.get(position); final boolean animate = (mAnimateSuggestions && engine.hasSuggestions()); - row.updateFromSearchEngine(engine, animate); + row.updateSuggestions(mSuggestionsEnabled, engine, mSearchTerm, animate); if (animate) { // Only animate suggestions the first time they are shown mAnimateSuggestions = false; diff --git a/mobile/android/base/home/SearchEngineRow.java b/mobile/android/base/home/SearchEngineRow.java index 667ab7902b32..c315557f227c 100644 --- a/mobile/android/base/home/SearchEngineRow.java +++ b/mobile/android/base/home/SearchEngineRow.java @@ -5,6 +5,7 @@ package org.mozilla.gecko.home; +import org.mozilla.gecko.db.BrowserContract.SearchHistory; import org.mozilla.gecko.R; import org.mozilla.gecko.Telemetry; import org.mozilla.gecko.TelemetryContract; @@ -16,6 +17,8 @@ import org.mozilla.gecko.widget.AnimatedHeightLayout; import org.mozilla.gecko.widget.FaviconView; import org.mozilla.gecko.widget.FlowLayout; +import android.database.Cursor; +import android.content.ContentResolver; import android.content.Context; import android.util.AttributeSet; import android.view.KeyEvent; @@ -133,7 +136,10 @@ class SearchEngineRow extends AnimatedHeightLayout { return suggestionText.getText().toString(); } - private void setSuggestionOnView(View v, String suggestion) { + private void setSuggestionOnView(View v, String suggestion, boolean isUserSavedSearch) { + final ImageView historyIcon = (ImageView) v.findViewById(R.id.suggestion_item_icon); + historyIcon.setVisibility(isUserSavedSearch ? View.VISIBLE: View.GONE); + final TextView suggestionText = (TextView) v.findViewById(R.id.suggestion_text); suggestionText.setText(suggestion); setDescriptionOnSuggestion(suggestionText, suggestion); @@ -172,7 +178,76 @@ class SearchEngineRow extends AnimatedHeightLayout { mEditSuggestionListener = listener; } - public void updateFromSearchEngine(SearchEngine searchEngine, boolean animate) { + private void bindSuggestionView(String suggestion, boolean animate, int recycledSuggestionCount, Integer previousSuggestionChildIndex, boolean isUserSavedSearch){ + final View suggestionItem; + + // Reuse suggestion views from recycled view, if possible. + if (previousSuggestionChildIndex + 1 < recycledSuggestionCount) { + suggestionItem = mSuggestionView.getChildAt(previousSuggestionChildIndex + 1); + suggestionItem.setVisibility(View.VISIBLE); + } else { + suggestionItem = mInflater.inflate(R.layout.suggestion_item, null); + + suggestionItem.setOnClickListener(mClickListener); + suggestionItem.setOnLongClickListener(mLongClickListener); + + // Store the position of the suggestion for telemetry. + suggestionItem.setTag(String.valueOf(previousSuggestionChildIndex)); + + mSuggestionView.addView(suggestionItem); + } + + setSuggestionOnView(suggestionItem, suggestion, isUserSavedSearch); + + if (animate) { + AlphaAnimation anim = new AlphaAnimation(0, 1); + anim.setDuration(ANIMATION_DURATION); + anim.setStartOffset(previousSuggestionChildIndex * ANIMATION_DURATION); + suggestionItem.startAnimation(anim); + } + } + + private void hideRecycledSuggestions(int lastVisibleChildIndex, int recycledSuggestionCount) { + // Hide extra suggestions that have been recycled. + for (int i = lastVisibleChildIndex + 1; i < recycledSuggestionCount; ++i) { + mSuggestionView.getChildAt(i).setVisibility(View.GONE); + } + } + + private void updateFromSavedSearches(String searchTerm, boolean animate, int suggestionCounter, int recycledSuggestionCount) { + final ContentResolver cr = getContext().getContentResolver(); + + String[] columns = new String[] { SearchHistory.QUERY }; + String sortOrderAndLimit = SearchHistory.DATE + " DESC"; + + final Cursor c = cr.query(SearchHistory.CONTENT_URI, columns, null, null, sortOrderAndLimit); + if (c == null) { + return; + } + try { + if (c.moveToFirst()) { + int counter = 0; + final int searchColumn = c.getColumnIndexOrThrow(SearchHistory.QUERY); + do { + final String savedSearch = c.getString(searchColumn); + if (counter == 4) { + break; + } + // Bug 1200371 will move the filtering/matching and limit into the sql query + if (savedSearch.startsWith(searchTerm)) { + bindSuggestionView(savedSearch, animate, recycledSuggestionCount, suggestionCounter, true); + ++suggestionCounter; + ++counter; + } + } while (c.moveToNext()); + } + } finally { + c.close(); + } + hideRecycledSuggestions(suggestionCounter, recycledSuggestionCount); + } + + private int updateFromSearchEngine(SearchEngine searchEngine, boolean animate, int recycledSuggestionCount) { // Update search engine reference. mSearchEngine = searchEngine; @@ -182,54 +257,30 @@ class SearchEngineRow extends AnimatedHeightLayout { // Set the initial content description. setDescriptionOnSuggestion(mUserEnteredTextView, mUserEnteredTextView.getText().toString()); - // Add additional suggestions given by this engine. - final int recycledSuggestionCount = mSuggestionView.getChildCount(); - int suggestionCounter = 0; + // Apply Search Engine's suggestions for (String suggestion : mSearchEngine.getSuggestions()) { - final View suggestionItem; - - // Reuse suggestion views from recycled view, if possible. - if (suggestionCounter + 1 < recycledSuggestionCount) { - suggestionItem = mSuggestionView.getChildAt(suggestionCounter + 1); - suggestionItem.setVisibility(View.VISIBLE); - } else { - suggestionItem = mInflater.inflate(R.layout.suggestion_item, null); - - suggestionItem.setOnClickListener(mClickListener); - suggestionItem.setOnLongClickListener(mLongClickListener); - - // Store the position of the suggestion for telemetry. - suggestionItem.setTag(String.valueOf(suggestionCounter)); - - final ImageView magnifier = - (ImageView) suggestionItem.findViewById(R.id.suggestion_magnifier); - magnifier.setVisibility(View.GONE); - - mSuggestionView.addView(suggestionItem); - } - - setSuggestionOnView(suggestionItem, suggestion); - - if (animate) { - AlphaAnimation anim = new AlphaAnimation(0, 1); - anim.setDuration(ANIMATION_DURATION); - anim.setStartOffset(suggestionCounter * ANIMATION_DURATION); - suggestionItem.startAnimation(anim); - } - + bindSuggestionView(suggestion, animate, recycledSuggestionCount, suggestionCounter, false); ++suggestionCounter; } - // Hide extra suggestions that have been recycled. - for (int i = suggestionCounter + 1; i < recycledSuggestionCount; ++i) { - mSuggestionView.getChildAt(i).setVisibility(View.GONE); - } + hideRecycledSuggestions(suggestionCounter, recycledSuggestionCount); // Make sure mSelectedView is still valid. if (mSelectedView >= mSuggestionView.getChildCount()) { mSelectedView = mSuggestionView.getChildCount() - 1; } + + return suggestionCounter; + } + + public void updateSuggestions(boolean suggestionsEnabled, SearchEngine searchEngine, String searchTerm, boolean animate) { + // This can be called before the opt-in permission prompt is shown or set. Check first. + if (suggestionsEnabled) { + final int recycledSuggestionCount = mSuggestionView.getChildCount(); + final int suggestionViewCount = updateFromSearchEngine(searchEngine, animate, recycledSuggestionCount); + updateFromSavedSearches(searchTerm, animate, suggestionViewCount, recycledSuggestionCount); + } } @Override diff --git a/mobile/android/base/resources/layout/suggestion_item.xml b/mobile/android/base/resources/layout/suggestion_item.xml index 49dbeaf44d2e..8118ef042d23 100644 --- a/mobile/android/base/resources/layout/suggestion_item.xml +++ b/mobile/android/base/resources/layout/suggestion_item.xml @@ -12,11 +12,12 @@ android:clickable="true" android:padding="7dp"> - + android:layout_width="18dip" + android:layout_height="18dip" + android:visibility="gone"/> Date: Tue, 1 Sep 2015 15:52:26 -0700 Subject: [PATCH 042/101] Bug 1197054 - Build changes to get rid of bookmarks.inc.r=nalexander --- mobile/android/base/locales/Makefile.in | 14 ------ .../base/locales/en-US/android_strings.dtd | 15 ++++++ mobile/android/base/strings.xml.in | 13 +++-- mobile/locales/Makefile.in | 48 +------------------ mobile/locales/en-US/profile/bookmarks.inc | 41 ---------------- .../locales/generic/profile/bookmarks.json.in | 8 ---- 6 files changed, 22 insertions(+), 117 deletions(-) delete mode 100644 mobile/locales/en-US/profile/bookmarks.inc delete mode 100644 mobile/locales/generic/profile/bookmarks.json.in diff --git a/mobile/android/base/locales/Makefile.in b/mobile/android/base/locales/Makefile.in index 7851f5ad89e0..e94e52a7a153 100644 --- a/mobile/android/base/locales/Makefile.in +++ b/mobile/android/base/locales/Makefile.in @@ -44,18 +44,6 @@ chrome-%:: $(dir-res-raw)-$(AB_rCD)/browsersearch.json \ AB_CD=$* -# setup the path to bookmarks.inc. copied and tweaked version of MERGE_FILE from config/config.mk -MOBILE_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/mobile/locales/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/mobile) - -ifdef LOCALE_MERGEDIR -BOOKMARKSPATH = $(firstword \ - $(wildcard $(LOCALE_MERGEDIR)/mobile/profile/bookmarks.inc ) \ - $(wildcard $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc ) \ - $(topsrcdir)/mobile/locales/en-US/profile/bookmarks.inc ) -else -BOOKMARKSPATH = $(abspath $(MOBILE_LOCALE_SRCDIR)/profile/bookmarks.inc) -endif - # Determine the ../res/values[-*]/ path strings-xml-bypath = $(filter %/strings.xml,$(MAKECMDGOALS)) ifeq (,$(strip $(strings-xml-bypath))) @@ -69,7 +57,6 @@ strings-xml-preqs =\ $(STRINGSPATH) \ $(SEARCHSTRINGSPATH) \ $(SYNCSTRINGSPATH) \ - $(BOOKMARKSPATH) \ $(if $(IS_LANGUAGE_REPACK),FORCE) \ $(NULL) @@ -80,7 +67,6 @@ $(dir-strings-xml)/strings.xml: $(strings-xml-preqs) $(call py_action,preprocessor, \ $(DEFINES) \ -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \ - -DBOOKMARKSPATH='$(BOOKMARKSPATH)' \ -DBRANDPATH='$(BRANDPATH)' \ -DMOZ_ANDROID_SHARED_ACCOUNT_TYPE=$(MOZ_ANDROID_SHARED_ACCOUNT_TYPE) \ -DMOZ_ANDROID_SHARED_FXACCOUNT_TYPE=$(MOZ_ANDROID_SHARED_FXACCOUNT_TYPE) \ diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 69d669dcac3b..fb027f9288f2 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -703,3 +703,18 @@ just addresses the organization to follow, e.g. "This site is run by " --> + + + + + + + + + + + + + + + diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index e88fb35d6a67..05d730d96ef1 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -19,7 +19,6 @@ ]> -#includesubst @BOOKMARKSPATH@ @MOZ_APP_DISPLAYNAME@ @ANDROID_PACKAGE_NAME@ @@ -453,25 +452,25 @@ - @bookmarks_aboutBrowser@ + &bookmarks_about_browser; about:firefox - @bookmarks_addons@ + &bookmarks_addons; https://addons.mozilla.org/android/ - @bookmarks_support@ + &bookmarks_support; https://support.mozilla.org/products/mobile - @bookmarks_marketplace@ + &bookmarks_marketplace; https://marketplace.firefox.com/ - @bookmarks_restricted_webmaker@ + &bookmarks_restricted_webmaker; https://webmaker.org/ - @bookmarks_restricted_support@ + &bookmarks_restricted_support; https://support.mozilla.org/kb/kids diff --git a/mobile/locales/Makefile.in b/mobile/locales/Makefile.in index 3b71ee64f361..ecf96301f706 100644 --- a/mobile/locales/Makefile.in +++ b/mobile/locales/Makefile.in @@ -44,17 +44,6 @@ search-jar-default: search-jar ########################################################################### -bookmarks = bookmarks.json -bookmarks-ts = $(tgt-gendir)/$(bookmarks) -src-bookmarks = $(srcdir)/generic/profile/$(bookmarks).in - -GARBAGE += $(bookmarks) $(bookmarks-ts) -# --------------------------------------------------------------------------- -# Note: Always symlink bookmarks.json to pickup the current build for a -# locale. Phase 2 edits should remove the common/symlink file and -# provide a user function able to derive the path. -########################################################################### - ## Searchlist plugin config plugin-file-array = \ $(wildcard $(LOCALE_SRCDIR)/searchplugins/list.txt) \ @@ -144,7 +133,6 @@ libs-%: $(libs-preqs) $(display-deps) @$(MAKE) -C $(DEPTH)/toolkit/locales libs-$* @$(MAKE) -C $(DEPTH)/intl/locales AB_CD=$* XPI_NAME=locale-$* - @$(MAKE) -B $(bookmarks) AB_CD=$* @$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$* @$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=defaults/pref @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$* @@ -152,7 +140,6 @@ libs-%: $(libs-preqs) # Tailored target to just add the chrome processing for multi-locale builds chrome-%: $(display-deps) - @$(MAKE) -B $(bookmarks) AB_CD=$* @$(MAKE) -B searchplugins AB_CD=$* @$(MAKE) chrome AB_CD=$* @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$* @@ -160,37 +147,4 @@ chrome-%: NO_JA_JP_MAC_AB_CD := $(if $(filter ja-JP-mac, $(AB_CD)),ja,$(AB_CD)) -# emulate vpath to gather deps with a path -has-mergedir = $(if $(strip $(LOCALE_MERGEDIR)),1) -bookmarks-inc-array = \ - $(wildcard \ - $(if $(has_mergedir),$(LOCALE_MERGEDIR)/mobile/profile/bookmarks.inc) \ - $(LOCALE_SRCDIR)/profile/bookmarks.inc \ - $(if $(has-mergedir),$(srcdir)/en-US/profile/bookmarks.inc) \ - ) -bookmarks-inc = $(firstword $(bookmarks-inc-array)) - -bookmarks-preqs = \ - $(bookmarks-inc) \ - $(src-bookmarks) \ - generic/profile/$(bookmarks).in \ - $(if $(IS_LANGUAGE_REPACK),FORCE) \ - $(GLOBAL_DEPS) \ - $(NULL) - -$(bookmarks-ts): $(bookmarks-preqs) - $(display_deps) - $(call py_action,preprocessor, \ - -I $< \ - -DAB_CD=$(NO_JA_JP_MAC_AB_CD) \ - $(src-bookmarks) \ - -o $@) - -.PHONY: bookmarks $(bookmarks) -bookmarks: $(bookmarks) -$(bookmarks): $(bookmarks-ts) - @echo '\nGenerating: $@' - ln -fn $< . - - -export:: searchplugins bookmarks +export:: searchplugins diff --git a/mobile/locales/en-US/profile/bookmarks.inc b/mobile/locales/en-US/profile/bookmarks.inc deleted file mode 100644 index dd25ad102997..000000000000 --- a/mobile/locales/en-US/profile/bookmarks.inc +++ /dev/null @@ -1,41 +0,0 @@ -# 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/. -#filter emptyLines - -# LOCALIZATION NOTE: The 'en-US' strings in the URLs will be replaced with -# your locale code, and link to your translated pages as soon as they're -# live. - -# LOCALIZATION NOTE: Some of these URLs are currently 404s, but should be coming -# online shortly. - -# LOCALIZATION NOTE (bookmarks_title): -# title for the folder that will contains the default bookmarks -#define bookmarks_title Mobile - -# LOCALIZATION NOTE (bookmarks_aboutBrowser): -# link title for about:fennec -#define bookmarks_aboutBrowser Firefox: About your browser - -# LOCALIZATION NOTE (bookmarks_addons): -# link title for https://addons.mozilla.org/en-US/mobile -#define bookmarks_addons Firefox: Customize with add-ons - -# LOCALIZATION NOTE (bookmarks_support): -# link title for https://support.mozilla.org/ -#define bookmarks_support Firefox: Support - -# LOCALIZATION NOTE (bookmarks_marketplace): -# link title for https://marketplace.firefox.com -#define bookmarks_marketplace Firefox Marketplace - -# LOCALIZATION NOTE (bookmarks_restricted_support): -# link title for https://support.mozilla.org/kb/kids -#define bookmarks_restricted_support Firefox Help and Support for a simplified kid-friendly version of Firefox - -# LOCALIZATION NOTE (bookmarks_restricted_webmaker): -# link title for https://webmaker.org -#define bookmarks_restricted_webmaker Learn the Web: Mozilla Webmaker - -#unfilter emptyLines diff --git a/mobile/locales/generic/profile/bookmarks.json.in b/mobile/locales/generic/profile/bookmarks.json.in deleted file mode 100644 index 0c1dfc798a9f..000000000000 --- a/mobile/locales/generic/profile/bookmarks.json.in +++ /dev/null @@ -1,8 +0,0 @@ -#filter substitution -{"type":"text/x-moz-place-container","root":"placesRoot","children": - [{"type":"text/x-moz-place-container","title":"@bookmarks_title@","annos":[{"name":"mobile/bookmarksRoot","expires":4,"type":1,"value":1}], -# Bug 921433: this is empty, because bookmarks are now added via resource -# strings: see LocalBrowserDB.addDefaultBookmarks. - "children": [] - }] -} From fc8eb5723149347f0204d62365955b320463d2af Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Tue, 1 Sep 2015 16:00:42 -0700 Subject: [PATCH 043/101] Bug 1200322 - Fix a11y problem around key navigation in the urlbar. r=Mossop --- browser/base/content/urlbarBindings.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 9506e8c381db..7797335b3f13 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -193,8 +193,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/. case KeyEvent.DOM_VK_LEFT: case KeyEvent.DOM_VK_RIGHT: case KeyEvent.DOM_VK_HOME: - this.popup.hidePopup(); - return; + // Reset the selected index so that nsAutoCompleteController + // simply closes the popup without trying to fill anything. + this.popup.selectedIndex = -1; break; } From b963a8d796ee65e70c8ccc30f5b02555018b36e6 Mon Sep 17 00:00:00 2001 From: Allison Naaktgeboren Date: Tue, 1 Sep 2015 16:28:47 -0700 Subject: [PATCH 044/101] CLOSED TREE missing space in commit from Bug 1197054 --- mobile/android/base/locales/en-US/android_strings.dtd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index fb027f9288f2..f58764daab61 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -712,7 +712,7 @@ just addresses the organization to follow, e.g. "This site is run by " --> - + From acf840af001aefc3ffc72e3290f3c5b3f7abe65c Mon Sep 17 00:00:00 2001 From: "Bernardo P. Rittmeyer" Date: Mon, 31 Aug 2015 16:42:21 -0700 Subject: [PATCH 045/101] Bug 1195949 - Remove key icon from context menu fill. r=MattN --HG-- extra : transplant_source : R3%C5%97%AF%C4%1A%8A%7B%813%7C%B2%F3%89%AF%BF%E7X%1C --- browser/base/content/browser-context.inc | 1 - browser/themes/shared/contextmenu.inc.css | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index cd7b2ede3ae7..c9b12849a932 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -420,7 +420,6 @@