From a829a473c2ad1a0f50b4b99808c3c2d6dcabc2a6 Mon Sep 17 00:00:00 2001 From: Antonin Hildebrand Date: Thu, 31 Mar 2016 17:24:37 +0200 Subject: [PATCH] squash 'resources/unpacked/devtools' changes from 791c2d1..3284500 3284500 DevTools: use standard tabbed pane and widgets for the service workers view. 11923aa DevTools: enable capture screen with device art. 3107a42 [DevTools] Fix bug with WebSockets flipping frames d3630b9 DevTools: do not run acorn in the UI thread 25c1120 Devtools: Add trigger add to homescreen to command menu b396261 Devtools: Remove non-existant empty cache, hard reload from command menu BUG=589353 d9247b7 Devtools Animations: Correctly sync scrubber when first animation ended c3bd2dc [DevTools] Check mappings.length in BlackboxManager on SourceMap loaded 093c952 DevTools: [SSP] accept editing on mouse click b270b46 [DevTools] Fixed context menu breakage when edit dom in console 9d536f7 Devtools: Add screenshot button to device mode toolbar. 974c4c0 DevTools: request app banner from DevTools without #enable-add-to-shelf flag while emulating. ab84c2c DevTools: [SASS] improve scss parser 93559b1 [DevTools] Added serverIPaddress to HAR output dea4302 DevTools: use standard UI widget for the service workers toolbar. 6eac01e DevTools: [SASS] simplify SCSS parsing 4f27144 DevTools: simplify WI.FormattedContentBuilder 8d1bea1 [DevTools] Add DebuggerScript.js compilation to compile_frontend.py 8c3efc3 [DevTools] Introduced WebInspector.LiveLocationPool e81e238 DevTools: introduce liveSASS hidden experiment 5747abb [DevTools] Remove promise inspector experiment be19483 DevTools: [CSS] reload sourcemap as SASS watchdog updates CSS file 1b214ef Added regex support in search for network tab bcf3547 Show v8.parseOnBackground in DevTools Timeline 3cc3585 DevTools: Refactor CPU profiler: get rid of data object. cf35d4d [DevTools] Use InspectorFrontendHost.readyForTest for layout tests. c87aaf6 [DevTools] Add a bunch of emulation actions to command menu. a02e528 DevTools: make Timeline flame chart nice again. 12affcb SourceFrame implements Search interface SourceFrame should now implement Search interface. 6b8f8a8 DevTools: [ux regression] There is no way to clear console history. fbd4149 Handle the case for older releases (pre M51) by adding CSS fixes in compatibility script to handle the case of a border-image element that lacks the border-style. 2c06e0c Revert of [DevTools] Use InspectorFrontendHost.readyForTest for layout tests. (patchset #3 id:40001 of https://codereview.chromium.org/1819243002/ ) git-subtree-dir: resources/unpacked/devtools git-subtree-split: 3284500608ac9e8363185d335274adfdb2ee2f43 --- BUILD.gn | 3 - devtools.gyp | 2 - devtools.gypi | 9 +- front_end/Images/notifications.svg | 1 - front_end/Images/src/optimize_png.hashes | 2 +- front_end/Images/src/svg2png.hashes | 2 +- front_end/Images/src/toolbarButtonGlyphs.svg | 15 +- front_end/Images/toolbarButtonGlyphs.png | Bin 8776 -> 8874 bytes front_end/Images/toolbarButtonGlyphs_2x.png | Bin 21473 -> 21703 bytes front_end/Images/updateServiceWorker.svg | 81 -- front_end/Tests.js | 2 +- front_end/animation/AnimationModel.js | 7 +- front_end/bindings/BlackboxManager.js | 1 + front_end/bindings/BreakpointManager.js | 10 +- front_end/bindings/CSSWorkspaceBinding.js | 19 +- .../bindings/DebuggerWorkspaceBinding.js | 78 +- front_end/bindings/LiveLocation.js | 70 +- .../PresentationConsoleMessageHelper.js | 11 +- front_end/bindings/SASSSourceMapping.js | 27 + front_end/common/ResourceType.js | 2 +- front_end/common/Text.js | 49 ++ front_end/common/TextRange.js | 18 +- front_end/components/Linkifier.js | 58 +- .../components/NetworkConditionsSelector.js | 31 +- .../RequestAppBannerActionDelegate.js | 29 + front_end/components/module.json | 24 + front_end/console/ConsolePanel.js | 19 +- front_end/console/ConsoleView.js | 20 + front_end/console/module.json | 7 + front_end/devtools.js | 5 + front_end/elements/ElementsTreeElement.js | 8 +- front_end/elements/StylesSidebarPane.js | 8 +- front_end/emulation/DeviceModeView.js | 51 +- front_end/emulation/DeviceModeWrapper.js | 19 +- front_end/emulation/module.json | 45 +- front_end/formatter_worker/CSSFormatter.js | 11 +- .../FormattedContentBuilder.js | 13 +- front_end/formatter_worker/FormatterWorker.js | 101 +++ front_end/formatter_worker/HTMLFormatter.js | 4 +- .../formatter_worker/IdentityFormatter.js | 4 +- .../formatter_worker/JavaScriptFormatter.js | 5 +- front_end/inspector.json | 1 - front_end/main/Main.js | 5 +- front_end/main/module.json | 2 - front_end/network/NetworkLogView.js | 38 +- front_end/platform/utilities.js | 27 + .../profiler/CPUProfileBottomUpDataGrid.js | 7 - front_end/profiler/CPUProfileDataGrid.js | 122 +-- .../profiler/CPUProfileTopDownDataGrid.js | 1 - front_end/promises/PromisePane.js | 791 ------------------ front_end/promises/module.json | 20 - front_end/promises/promisePane.css | 95 --- front_end/resources/ServiceWorkersView.js | 367 ++++---- front_end/resources/module.json | 6 + front_end/resources/serviceWorkersView.css | 188 +---- front_end/sass/SASSProcessor.js | 2 + front_end/sass/SASSSupport.js | 101 ++- front_end/sass/module.json | 10 + front_end/screencast/ScreencastApp.js | 33 - front_end/screencast/module.json | 9 +- front_end/sdk/CSSModel.js | 269 +++++- front_end/sdk/CSSStyleDeclaration.js | 2 +- front_end/sdk/DebuggerModel.js | 59 +- front_end/sdk/HAREntry.js | 8 +- front_end/sdk/NetworkManager.js | 2 + front_end/source_frame/SourceFrame.js | 85 +- .../TextEditorAutocompleteController.js | 2 +- front_end/sources/CallStackSidebarPane.js | 15 +- front_end/sources/SourceMapNamesResolver.js | 203 +++-- front_end/sources/SourcesPanel.js | 8 +- front_end/sources/SourcesView.js | 43 +- front_end/sources/module.json | 1 - front_end/timeline/TimelineFlameChart.js | 22 +- front_end/timeline/TimelineModel.js | 1 + front_end/timeline/TimelinePanel.js | 4 +- front_end/timeline/TimelineUIUtils.js | 16 +- front_end/ui/FilterBar.js | 17 +- front_end/ui/SuggestBox.js | 20 +- front_end/ui/TabbedPane.js | 37 +- front_end/ui/TextPrompt.js | 30 +- front_end/ui/Toolbar.js | 10 +- front_end/ui/suggestBox.css | 24 + front_end/ui/tabbedPane.css | 5 + front_end/ui/toolbar.css | 12 + front_end/ui_lazy/FlameChart.js | 20 +- protocol.json | 52 -- scripts/compile_frontend.py | 16 + 87 files changed, 1567 insertions(+), 2112 deletions(-) delete mode 100644 front_end/Images/notifications.svg delete mode 100644 front_end/Images/updateServiceWorker.svg create mode 100644 front_end/components/RequestAppBannerActionDelegate.js delete mode 100644 front_end/promises/PromisePane.js delete mode 100644 front_end/promises/module.json delete mode 100644 front_end/promises/promisePane.css diff --git a/BUILD.gn b/BUILD.gn index 7b22252a1a..e0294a700f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -41,7 +41,6 @@ devtools_modules_js_files = gypi_values.devtools_layers_js_files + gypi_values.devtools_network_js_files + gypi_values.devtools_profiler_js_files + - gypi_values.devtools_promises_js_files + gypi_values.devtools_resources_js_files + gypi_values.devtools_sass_js_files + gypi_values.devtools_security_js_files + @@ -150,7 +149,6 @@ action("generate_devtools_grd") { resources_out_dir + "layers_module.js", resources_out_dir + "network_module.js", resources_out_dir + "profiler_module.js", - resources_out_dir + "promises_module.js", resources_out_dir + "resources_module.js", resources_out_dir + "sass_module.js", resources_out_dir + "security_module.js", @@ -282,7 +280,6 @@ action("build_applications") { resources_out_dir + "layers_module.js", resources_out_dir + "network_module.js", resources_out_dir + "profiler_module.js", - resources_out_dir + "promises_module.js", resources_out_dir + "resources_module.js", resources_out_dir + "sass_module.js", resources_out_dir + "security_module.js", diff --git a/devtools.gyp b/devtools.gyp index ab14a9aa8f..bfb3128eee 100644 --- a/devtools.gyp +++ b/devtools.gyp @@ -115,7 +115,6 @@ '<(PRODUCT_DIR)/resources/inspector/layers_module.js', '<(PRODUCT_DIR)/resources/inspector/network_module.js', '<(PRODUCT_DIR)/resources/inspector/profiler_module.js', - '<(PRODUCT_DIR)/resources/inspector/promises_module.js', '<(PRODUCT_DIR)/resources/inspector/resources_module.js', '<(PRODUCT_DIR)/resources/inspector/sass_module.js', '<(PRODUCT_DIR)/resources/inspector/security_module.js', @@ -283,7 +282,6 @@ '<(_output_path)/layers_module.js', '<(_output_path)/network_module.js', '<(_output_path)/profiler_module.js', - '<(_output_path)/promises_module.js', '<(_output_path)/resources_module.js', '<(_output_path)/sass_module.js', '<(_output_path)/security_module.js', diff --git a/devtools.gypi b/devtools.gypi index 27a3c6cb7c..04ddd729ee 100644 --- a/devtools.gypi +++ b/devtools.gypi @@ -105,6 +105,7 @@ 'front_end/components/ObjectPopoverHelper.js', 'front_end/components/ObjectPropertiesSection.js', 'front_end/components/RemoteObjectPreviewFormatter.js', + 'front_end/components/RequestAppBannerActionDelegate.js', 'front_end/components/ShortcutsScreen.js', 'front_end/components/EventListenersUtils.js', 'front_end/components/EventListenersView.js', @@ -337,7 +338,6 @@ 'front_end/network/module.json', 'front_end/platform/module.json', 'front_end/profiler/module.json', - 'front_end/promises/module.json', 'front_end/resources/module.json', 'front_end/sass/module.json', 'front_end/security/module.json', @@ -497,10 +497,6 @@ 'front_end/extensions/ExtensionView.js', '<@(devtools_extension_api_files)', ], - 'devtools_promises_js_files': [ - 'front_end/promises/promisePane.css', - 'front_end/promises/PromisePane.js', - ], 'devtools_heap_snapshot_worker_js_files': [ 'front_end/common/TextUtils.js', 'front_end/common/UIString.js', @@ -775,7 +771,6 @@ '<@(devtools_layers_js_files)', '<@(devtools_network_js_files)', '<@(devtools_profiler_js_files)', - '<@(devtools_promises_js_files)', '<@(devtools_resources_js_files)', '<@(devtools_sass_js_files)', '<@(devtools_security_js_files)', @@ -823,7 +818,6 @@ 'front_end/Images/localStorage.png', 'front_end/Images/navigationControls.png', 'front_end/Images/navigationControls_2x.png', - 'front_end/Images/notifications.svg', 'front_end/Images/paneAddButtons.png', 'front_end/Images/paneFilterButtons.png', 'front_end/Images/paneRefreshButtons.png', @@ -867,7 +861,6 @@ 'front_end/Images/toolbarItemSelected.png', 'front_end/Images/touchCursor.png', 'front_end/Images/touchCursor_2x.png', - 'front_end/Images/updateServiceWorker.svg', ], 'devtools_extension_api_files': [ 'front_end/extensions/ExtensionAPI.js', diff --git a/front_end/Images/notifications.svg b/front_end/Images/notifications.svg deleted file mode 100644 index ec2adeddc5..0000000000 --- a/front_end/Images/notifications.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front_end/Images/src/optimize_png.hashes b/front_end/Images/src/optimize_png.hashes index 25464fd48b..36fe3b8b14 100644 --- a/front_end/Images/src/optimize_png.hashes +++ b/front_end/Images/src/optimize_png.hashes @@ -3,5 +3,5 @@ "breakpoint.svg": "69cd92d807259c022791112809b97799", "settingsListRemove.svg": "ce9e7c5c5cdaef28e6ee51d9478d5485", "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28", - "toolbarButtonGlyphs.svg": "fcf3159499e07a580dc9bd827d4f627f" + "toolbarButtonGlyphs.svg": "304b6a326639d26c4710d57205413323" } \ No newline at end of file diff --git a/front_end/Images/src/svg2png.hashes b/front_end/Images/src/svg2png.hashes index 25464fd48b..36fe3b8b14 100644 --- a/front_end/Images/src/svg2png.hashes +++ b/front_end/Images/src/svg2png.hashes @@ -3,5 +3,5 @@ "breakpoint.svg": "69cd92d807259c022791112809b97799", "settingsListRemove.svg": "ce9e7c5c5cdaef28e6ee51d9478d5485", "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28", - "toolbarButtonGlyphs.svg": "fcf3159499e07a580dc9bd827d4f627f" + "toolbarButtonGlyphs.svg": "304b6a326639d26c4710d57205413323" } \ No newline at end of file diff --git a/front_end/Images/src/toolbarButtonGlyphs.svg b/front_end/Images/src/toolbarButtonGlyphs.svg index 779809e51e..fd43823dc8 100644 --- a/front_end/Images/src/toolbarButtonGlyphs.svg +++ b/front_end/Images/src/toolbarButtonGlyphs.svg @@ -21,13 +21,13 @@ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> \ No newline at end of file + sketch:type="MSShapeGroup" /> \ No newline at end of file diff --git a/front_end/Images/toolbarButtonGlyphs.png b/front_end/Images/toolbarButtonGlyphs.png index c19a2bf2ae35c7937ec6d824941a24f44bdb1bcd..fad9b5ec3097973e08ba9ca6eeaa7af68107ba0c 100644 GIT binary patch literal 8874 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

TWQ(FwRaR!BR|CO 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 diff --git a/front_end/Images/toolbarButtonGlyphs_2x.png b/front_end/Images/toolbarButtonGlyphs_2x.png index 0a8df07151a80c48dc6acdda89ba37fa4ff1ff43..939de055b229a96f0aaa04942050e4aa8889fb25 100644 GIT binary patch literal 21703 zcmbSyby!qy*Y42W(j_1wAgQ!~v?2`yNOuejDJ??^D5=sNQbP*^g2aGGNvO=wF^Gh8 zcbtvC_kGVf*NN}CzRe%AXU~3~XRWnY-0OZeuXVK4$cUMUK_C#>(**0Y;dl(A(Y!%LbmAT4a1HJ_clE zLvS^OsqE2ID0HzEA39{<6|n2&TEqcE!I zVmAXBG|Z*6ZOneU+qIaaaz)bMW@)Hp2;uP8 ztB6|N*)req%Ynagy{Nz$NSVZ%mhi3ESUFVzMyX62o-1$rBTTDwmrQKWA@T5is%;v| z4s&8_Pwof5_;eqB5mNGqoyx-aA5XQ{l?i!FeGy0Jzk}3@U5s+9*sY&8pWIt zxpzo@9*;Ylhehkx*Iy@JR#)vg0>PLV@acl0`60+%8 zfyjeS>(r9YT;kxw#-tO^KU7`~CmJ%&5kp?t0=6p8VzBuLCFe(Cz3$X0IYZ9-l{ zjY@}g^K_1BS;H6WCh#qd3fdiAdhD9~NX2o}#4b!s_-E%zdr$~{hVWi+6E=>=r@tCV z?BCf;s-pdPS8Ofk=zBh2aiU2-%F%_=Q2yG8dw_ zBfHu`=?+2AA)Uj3>!pwN-5u-Ijl(*s*G+A1)3<*931;|Cziz1@ug8-XLdAkpdzfib zp_nS*IlTR;MN#u^Dcezqxa+2lfWFcI?H@UxLcV3?=^k3=Cj!s@P!8^x1`^6u3hKuM zf#?$CJIC9?eIvY5G+m;~hlBdQ8iEIH2c8@87hO+VG(q1?uF2jn(!z7m3iF1!8)p1& zQm_RzTIlc_lwEw5y~7%AbRzMy5ux+~eirYg(xQI~xj&!tb*%1KQp{dS_{;Whr5#b) zd%~axpe$7%N*@b@ zvILhL?pCqp!j>wrxE}y(Vl7o(rMtcI(i&iUwSm3L18FDjFX7C4Y4O&BaJbAe@Eoeg z(X8G~eOM-8W?*;1SYN}qsUJ90k*q9_`{07kU872ekOfB=r?y$QnNi(^z%kKlY}r;A zsK7SNFu!OGG_{DKT?#|w(CY+!@!f#ZV4K*#z6{wr7 zZ;W@!`{^QAlMowB=x`!1l@6s)4#k?-FmQ|dIn~LyC%2mM2kR40>a@a^(Qu$naXd+x zP=(H;ax;r($xk2QTAE`H5_MkcHd9~rFoVqhG{n~ho9`aXQJ+$>Tj4cuW1m&fE` zQ=RCKTwEDv=xN7{j>l~-syAfCVi;hZDjuXrGu&PkkA->s%}HLlVV^o^7UsR6TLc|O z#UoGW?k?rUl8#XL1IMyD-rT~UWo$c5b>mEJwt`~!b2_2lfhj1M0&83tbHjNuAOk%-O{twMvl;qM%{XsmR^pTCtb@)55}B<;ZH_L(Nj;IrzQ^VxZ$ zESC6C7N!pjf(M?{z=6ELUeh?dqs7rMj?lEuX>6=NGq@!x-p8ir_J;LiuqY`~SB^={ z6hn~}jeg@y-bvuP`8`;*nY!)6mQj=O@w7{|zsUo^&oty8UqxYN!SWa3Sm_-8gP+so z8n4fCJ}5OB_GPxp(8(4t85BR9$Q))u9LK50`e}L8vDHDTnzlHbaL|N z#i^Bs)R}g%)go-XTbb^Ap9nm}rI<2Nh_yPwD}CJk2sg3f8(2e5a){$3sfsGXVrg%x zgwH;!$+zasPZq>KHD*i@@wL4Ks)s$D!v9?e&AJ9bnB)`*8a3UzzGL9>^-ou1#12lQ z`(T36XPVru+MXjc6IBc_KaR%-rJcwYy{~253x6nsp!3G4icV}Lj|g>m0dhyiX&VoB z+dqU*Yr@x*-(g!X&qxMkI~O9#gc$do5TBxZYkh*pCy%RLmRO!Xq${DZyG5zyYPw02 zlg)ILu)>HgA3!^wx3orW$`)e(rvS!_u0ah|zmlb}!?2<-+VBP^1M@Xig#Koe@05#q zqvoI?9YZ(|$}k1|zJIEb=4!{qMUANM%r83q15l3%L9}c)<3`F(ZkOW6&U}SDh8oJk zXV>b+@m-l$#GG7IDI~#AJ$VCdIMw>oAn1E24OxES7a=6&g%qt2s=`7HZq}s=uBoEM z18_siG)UjF?@`;k5Xac-wykc>bTi>lFRO27zuD zLC|Jk$02Z_rX`MD=@|(vKL`|RB4#O8rm}{MP`0n)KAPg3c4*&>;5+bKs_fA925SIw zMr>mvjK$zIXi;=!YQ_7irK2g)>5?Bu&yxxjK50R9E+L6bdR${kZDa1!PSb0sO4a0V z>w>h>z~ph6?n6O=tr?tr%WvcZI|7Sve>r?nWH+nBr7R5d}T zpn2k^MU!KX1mAH;mg1-EOT+O|4(Ot(Dz+dfveDBbqR|0@C{vnKxQ@*%wOL4TUci|v zVAB({rkG^<6Vbf(WsPe6%aIAwkS;Si9+EcYzNu%r;WBkMMN6-#q76o+eC{S~MO_x=2imn>UU~n--zRT;BlsxjZW6 zn~Ne8{9G)f5-PJ$CF1H*I?Qqy<5#POdd|m2ij=d`Y5r3z_LGp^aQInGu~zK$dDWA| ziALv-x~zu7>-l`k6axWm@G%0|uzSS7WPXcYPK7i|1#Y^jvpAsn^AmI$dhR=6!Vlfd zZZlGSb=S!1Bv{o4?&UMpenpwBU%qFVvVWHSh6JaU0^`W~^^u+jp+;g{ix%s@k`kXU zV(m3HyX$OCi-E}aH0FV>l~Zno;zLV&a6JSOc4+RKJziFt%wJ#XIxlFtZf5%Vx&5ZB zlAa|7ClLx);0B)B3408x!oW#itrh2`F&bVb48kErT7izLou4Kh#m{rkiCpHGl$F4L z9C-#n3157-tW1T|AxwNGFfHgj_ksSSS%{0z#FW61MJ!I%`#=)Xj+9>%oo#hjUgx=f zmY?zhEJHMN+$`lsWH`>ih`XtlM^u&UcvqBsPO;dMc$~Q|CU;TeXjQZwv|;@2g?2bJ zK0UY+dwyEbJ^uQGV*TSK6fwGFtUMMacD0jL%hfoh(&(GjW;8q}gR2Qa$!r!VfYl@X zq`S)?Z#L;KDM=u>7R?aF*T zx+BGVhI?@dH=_@9xk!5~e`#JaY~ZqSz2dgr5bbR5;xj$vveA}`cfjkBcN}d|%4e@1 zKKfF+P5#!o&AG~TUP{dIpX$S+)@$8%U7MWwvL+Ew5ix-kGYrgPVc-_%9PbZeCV9Cx zU2u1FA%xo1{qIGPeCJ>Py?O{addSHihPN0hTQbCIbt$M9N$CGBj-3N zwhAJubs1q&I8cWUGFCOe-?7R*a_-_Ab|$gZ8WHe0_;RGAc5Vc1$`Gong0quK*l{hd z`_GzJ@}-tW`iDO5^UV4O9ZJ!By?zO&DO~szpOL?6hp&p3`fQI-Ofw!>h zZ9TWl*>W4KjXn9Vnpc17#@?h|8;p;Fo3-}GCaM+R>c*l((eN?&;hMve1E*uE*x{uQOv%4Myl6FL)IWIA$-7? zm%nz>1~i4Ls3@#DyaL3^%OQJ5Xpc9NvqMeO!UtDby!Jk*@tsA7B zC3rn}TQzIHjTo3a#|1O@u}f!_?#g&IK@8qcR%Ro34lEBk^kZGm7fELBoN}XoTZ)-D z%o{M`V;+Z2V;KJ!x@9_(c~F^w$&m!$`-VVACzQN@7N@>;gq=7V`~<4I?|8c6_u8a- z3U#_>gV1iy=o@RFn5!6?3+lgr?fu|GHu}NP{M&PIG+(irl*_$U%Y!*fmwAKXb+7ZO zeu#2&U+e#XP6K16O@(a;QDP z9fQR{id=vjsii!d&^5(DpWG%D3_ovtqf$3Y-yt@H1#Lfb_@RHB3}>hE6bGj^Ezms2 zX4)RTKR2GI&*Q)H;!ceIZ6t{08;=svS&AZp965tuW%|`7x5Z+UcSmho!T>1!!!9)~ z5MOp&0H`jnWMeNZ&9*On9-uuPQ4IMdFTi~ji%HN0!cKC-P|$jcJ{wy%g`&$}5naX* zjJp0Ux{#Er+p#ycrY9#DPO><^9f-uE@4T#l*F&$0yi~53Duw*JTHhRtJCPzW;!e3W zylKLlB!b};)oL83O%>_EP1q}Y9kLTa;oi&-ujjXYwZxkgJ|o*J02rPz2gkSdQ2e>uPv3KQ(B^7AaxcCgOF0aPnUO8J;o>|0%6kSOqtuEr3@KXf_ zzY2+$md%_aG40>4E5?O})i@r2BGfp7=BmuP8iXi|ISBP;lZd;j_Q5VR}oFTN0Lz2$jNYFauLufkU zWCfT0kFHM<)3bici|5;ta}@(VsZBj0MjYjc0EY}|{VH90xZ7c}S4!X5S|eh7B1oYA zxSDL^VbfbqcAc@`Im@-YozBcTbI~Qi1($`_-a*05fe2(k=lF5B^vM3) zchY2KYHEnjWt;Jx^x?>y$OR%$OKN?HNbdA7dR_rs6Yd=qXx`k)X0*=`>UjJ1M-m%= zfXaunM~3mEe#As$$kwbnKTFacH+Wfai8Dm=0Z?8`z=$7ewW&j2?uM{^;}^})1pN%1 zCpUE|uF3&yRrdX%<{qX94+57asT8a9D!4)%mw5%S8dexE34?eDUOs6S{k*4X1?nLn zChhbqGrn)ww#Kt2b@=HlnQz(VFgdWRaTCl__8kphGCY=yixuui0E?!So0PG%t&?j!h1as59E@<|6mhGD=gG3Ljw0&NRXTiMmP)r zv%)FVzkwOqS(-8Zw97g)%XhkTXw1p5nJ{WT&?nJL1)|gLi!Ktcz`9P9b@`qk|HQGP z4*Qy7oDU?a&@4JChvAe=rznT=6?OGc&iorKS|70>fReIOLB9Sn2wDXlbvUoA75fP1 z*vVL{TH{z6Usgs1v&6D$x21;-Qb7KhV7YoUhU z(EVdxLC_57sMGoVS}_}gA}!rnNbH#mLg!Q!l{8g!hQ5fox=N64rjO%i zP-Z9zFjsf^)5CJ3^Mpj{nt& zohf-FU>Tp#KYj?Sls{R%JuG+lD zM$nvK-uN}=ZK3=Vf=*)sVExdWA!Yk)UUfci<`+YBo*76r22y-0vjvf>l}Z<8A99Il zYmR53zYIKIT>Ax#iuuo|3C!$@glW(5NfRU|6JF_;han?HTAzk`1$wmGw}QpD6yy6UICXW>T51@ zCQT(yl>t zVCcTdT%BA^YK5dHG45Zj9?!yilKH<|`!tzUypdA4ENYsUFK6)!1C>6Glmxta&cAUa5#xBmt2Epoh*cpzO8IG%N;y z*yg>~JsFFDgSmIL4xy&|pT$|C0l4;;iPBH`$lLjVOd1MxJp8&FL4l7splByWg7QG~ znSNJWV7{Ufm)BJ^eE3!m%XPP3Ola3)vmG#3@QF-A~qeN@)dOGS&K;x9~KBmRZ*5cgXsTB;)ulp zbVuwR!Y?&qLBI~vac$hY$;RMDArR;uJq|!hqBko34?+jnRRalMz}Ie4Y?NgRCGhdz zK8E!v_>CM)Zvhif<`AUR>&fHd0OvJ4tgs5lzuR~?wFbSEq#zHx3PHw0ZvG-lb_r(B zi*lCG_Y|Ekuccss9p0AdAK!DFwXNY0Bn73!VM(4+4j$(%sy&PtIz~+!Ah2+DI1g>F zCpHm_V6ie?fi)YSl1!S@5zn7BJc05F^2X8c$sbaYc5=K7$@#j=wbt2FPUTYt2cvy3yIQG0>n{G*Ho8=j%sUQ z439}St;7}NW(&$SFl`~g;J)HH9g)ECV7|F`;iu+tn038e0l>MsGc8I z&=G5X`V&V<(mX(KoZj*jEdpZZ@>#np)&tt0&Bm3VCq)*AwBLwWMdcF@w+GzwH$~_@ zJ9JGl|4)lUIfTZNi@`b?hx;hlP^{PvI`&UI4Y8!2)u@+{ND_$$ti;-MHIYd6jg z1CB>{-ZqLHNd)^VNYUpfKyr&kGK6mHRX>*35BV@qXMEYgxgN~V4Or*F+ALTS)G?hf z8z2HYzcGT}83D%bMiYRUHowyg@b$ph`BJkm3!Ndp*Y<0toa@!vKR_FPr6y*0I6F4m zi6t+{ftz7|~;GP zFD0<^0UEsdz;cV|0kK5HoxMxZ-L(c;nEY<-Ox?$u|NE%89;Io^2j36?O(>%s~+0lXWC zGEg=)ayJ6_RsQbH?wiL{B0Gpa%=5yd{#Ne@=q95OAfZS}*bd3f1k2|myFjX9c}Br6 z@xXZ#d6NRSvkN#*tWBA3&j0@h<>%{>x#mz6aCOiH4*qOPnqK|0Npr|U#Af41uTs-z zx^>UiI=|ID6r0JI>h#JgjBEBQ{TLg->ykI{v)0y*@dJ0-LZKP1sty&_emFMqYdQ<% zU%p(HS{dA~Y(F*B+tF{MQI&7MXMI3pcsH-y z0EwWrP3M})F4MYdE0W2gQ!$5r6g|}_#;&en_`>o7n}(U0nUbDfVjP0#@g~ktf2BC{ zg<&~nUMes6=sB`94<>#5UR&<9Wm}*Rhkm(EOwi4!-*4-Pt8HBms4Rx4?r)KH*95!Pvp`(R)%$NBnBVp)FNLoaK z6vK2ZtePItT&6pAxORt|+kEI9bAOT+r!LnFOX_sRb2l!=pHJ^VA~oLMIcpJL7BHWh z_ML^6;Dj`%4V)d7?h#z!18YT!40f8$Ch$d`l5mJpZ+}jFV-XaeP-Fhgk(zo{!%Gxg zX{x63EcJV17|tIO_B_%-+Q9lTRL;{5`7Nx#wjHuJ_g?2m(hwzkiN0#TfB9~1rYRDi z2mjqRebq;Q3yFdU+?K@No{@c$RDSGW+ICg?H{*JmD%j}kCnY4@>nC&6(ixITZ@6BH z3L827W1qX8Gkw*sZB-rIa)hJyzI-Y3pVTwA5`^f)vUOP%y19-Yo-wlxP&GUX;m?a4*XVq0YEG5AR0T z$zQ#=62Gq%W?p(ne@_n0s!-orCs?SW2kd~#Z)|U;QjBzKIUMtruQ5?s`Sok9_*ebN zy%M7uK9tju3a3W9#>A#B*2;KtW!TQtQ`rm%&!!zySl`blYs~xK19o3Ii!bs&RJ`*t zK{@98UYY5Lxn#qeRe!VKT80o+`Rnf$#A0Q%32*-%PnqN)IzK=CplxIvEnCguHfViW z&Y=US@>rN5^~HOUQb~Cr(Q?m`h*! zZ1A^8T*0Wa7i;xN9gd|+1ZiW)7a99iV7IQdP&tY zB&%DH)T-R~>*KLxzmv$migj(@%v2D;GKU{)r-L$}w^qX}R(V_RY`wLVp(+e>Zn%Ko znfSKkC#*!?Eef5v-2qKEj$&cB|Kd)``p|O2=OHlVR8*+Rfd^;D@)M3>wlCLL0v4_@ zd5(j(QH9YuD^=Ef36-A zw<#xQAmYQ9lP*3a{M^V)VO4iM+S4-QBIABtf0~7$f5!55mbUeI*T(&cCX58kFKL?NCyo~^Av;mB9i<(|95!_&ZKI^#%hCLVvcD-7?d%SsStN%Ctu!cCRr4-7K5)c`(?8r&2N=J~iYAP8)9cODGec_}SYS`iI@( zk#{$9l}DE4TpYc&ew%3Y=h!$xwhS^q1#zA}za*6XG)`E6%Ojl}!X5J~z4A&~Dv{Y% z?REj3?DuDVeSNFMQq~j?3@+M59-yO^=*7xX-g>^Rdf!7(s+IF3I;~xWn?LocXW)4C z#g>~iBYn}#D89Eay;#EW51O{RC>`#xmq8xC@;>ih%?2Rwd>3q_^X!Z2=U=Yqy$~Vr z)?M^xm;J%)Lb&N~(3hQaq+@2Lp}v-4700U(UoZ7m(Gj?ol$WRdKd-f1vz}Zq*g)3K z2SuW!NDE4ca`ELy%)UT z`|$V50^cZmvLfroXQWMJJXTs_G?1?jmcK0M3UGTfFI#k}FuxO&8H3~N4g(bh0x=U0 zIk>or_&=!7Y*1J@n1$+caV<(u-<8j?Jkfr}TK?)gwVu9y4X!PzHzlg9_eOj0ufVyC z5T`u@1WM6qbA61flcByQmg!J3(3Y%#HIayMc*o~%SrC55oe|sm@2;m@?Kkpoj?Ql+K^3 zCcQt=} zZHunfZlAM=W}9&-(h08*9&yvdWw_J8q288qZ%KUoj)ilL62$@a)>Gm7ta9ro1*%_F z2m~c{Hjx1YaD!}p$uD-IiLuK$34>W0Qc7eW%>Mj%>Th`_@`toJ<6((=JvHX;1bbX= zFXPXCPuXj7#thWT-NQLK+3xa*t+PC^a8@8U!!gTpevh+IWf%hXem=KN|NPL0uQ5pO zdA!A;y6~3NphEnpg)xo`kP zI(+h=M$chh6sY$og$Nkk8EeMn{}l_Ml!oP^G@UMdxc4<_L7^?%L+|O!O zT7KYn^1TTx%Xt&Ae{B6iqnnqy0zyR*L+P(NBG6B zO9-0q;AGg~PBI3Mp5EqmUBU_Fb5qNA3*EjKr8u9W>-_(juW*-fI4PRpey01JO`kx1 z+VR(_y$Vjmb0QC#O$t@*pG3<8eV;Vc`+lo5TYKlIy9BP1s{04mmHf-c8tBK6CW=qH zECU6mm@nA1ip5r5(erwqs%sJ%_6sc+{$uY*X*N31B{FwA4TPh;) zdmWKtOy?OvCNNn6WCjI=R~87c#9o_vC}LvUh6Ttv6U^uIuO*j=JZxVd$(INq`D`-$ zFUm!>U|3rAeM)j4H6PArzZhLnR2On5!J;@g3eFe0hCLRFwlr2-dThf?1Vl)!#L@gco0;3ly7un2Lb0aD z4860aw}<_8Lbb24aFarBS@9wX&)$*#&pRgdGLhd<$bezllLR-KQrSp$iiM+P|Z+W(NQf< z`(q{Sa%~~LPbOlR!YBXCe{nhI^kw?s9Z|;M}?u2&w^O^=hw$1h?rlw)h2(f3&p`-qqg$!-;_RV`BYj# z=VB#LkP!V!BZwsO$eCiQROyosN#wUtwg&BJrwcm5s!Q~mz`c-edTTp*@gCoU0tGS{ z6+YyL(j{k{F^8zvt+%pHd&OK((;o|sRpwr`DS5%?N|k3S40Ts9jh2{>gIgzizYS3$ zn{S?duqcek`L=16;wyE@`;=-$(()JE^SF>4dTV7%>d8!h`4)zGb^7kVSpBgO1<6yH zWlAN%m=x?8S1!B>oSLT=%l}erBs%aQqHwl6FvnUF*T0{Iw)Vt4h9*?0>&|U=Y zptuLwZ1FEGQ%CG3M;}g<$EH+C)>uW-u2Lh3>GaQ6$-ZY3V^KG_-3edOi}8IQ%Zo0N z&25>RCuVvTD{RIdJJ$F2k-3KK;;n}@U1zD!Tf$$P!0I-!%D6wZv)$xnZs$burqtoU z{-g_=X&hnB&VQ0Mx7}24e)FR>QlJ`6Cn$IapcsYkbqZdyR%?xc*pjSBlm_uj5FXmM zJF7CtNJSR99)IkOiew{XaW;klT~Ps5>cn>gj6;ghegFWmBEamu(E zyv5F~$SUnE{rVPTk2Hxz?sQD-lf*p{{u})iB#ah%*2*yRJerMRdh1VeE8{V^w(+X4 zG4)5Boi}}(`|VDchKG`cY5V@4jDk~bO}O;bwr<+5SO85jp~l7~7^!EbUL09no0hpA z%pQ8J{*bwc(5&z-`>_#w=k#2(Uh`?y2wA__Xl(QuO?t8L{&hgQ!@an^b%m<;LKW1n zudL&0z7xLP3F}LRE%_Z83?IH&wi*wVN(p97sXmqk))2SlXnr?YeV}2ZUJME{3oQmo zOv+g<_7-BjzhDPtMBUk6q7QQH*n7uu3hp6$dZXBpkJ=-&Ob|*^X|E!V`JL5GfJ*1m ziN+go(9_XD25M4qi+W~j6o>o!eMd-vO4=w|i*nfb^m=|z{C+68{JtY!@B&O)x?YGi z*AKL@z(6a!%_n0tpc@lwLhnqm#h74ZDrq% z61u&AW^5@s)}~;c2ZUTLh@RQ6#2W;J+8;alNrR{2Yql{#65mI%gt!JfXB@7AaZiVI z&bUR5gb9j4F2{#m679tz!EN6|l7ria_?2jxSa^}5jNXd-$3g*B2@R>mrA~i!R+S}a zt5an?g}G|LcH`34pRDgD8)%-Q-&vu`R`MmrD5rMIlDKaejY5;UpkhO@KQ?#t5IDyciMb$jbYpt+ySK&)W&%yx|v$iXGKqo!g;mcIka$Rl!@c^X*A59HDub#$U2#?yk)4=cSIHk(y_q7q{+)P8u>?(kUV6B{-CU8b zQEU{>*`Y{W>uV_lTQr3q$P!HV#bo&c21K+&bPcFkiX2=>U6nEKt;_HW+KndO;3~F{6y9hC0@jv$gdd$Dg^^i~Rs~1D$ zxhA^O&jR?fng3q=aS<_&Y4m%KtM(tZV=KuDU`cd&L|?PN>HItvGb@!Bn@61%%T`Fm zD2#+K3G?xn&C@V01V{WV3Zi%>uq`EtXf?h5y&4L8ojSc$k581#6}RNDY+Le7lVW6Q&w(o-g&E_6wcloGi_+vrK#aySyQQ8ysFuOr=(>&ehsQvQ$ zYr`*?%aP;DRE)QT@4RZ6VR!#r?HBiam7+WK#a`WxPGOCUZNi_xD(Lou#q#9i-0PN$ zVCloo4x$@|FiI+YJ#4%A8$$^FURD*#fj`7AbvwiCU(Z1S07gWO0@;>tkD0#x9`IXb z_*O}EQ^~$3S&|XuN;$1WX3)xO2EEoAY+|6ZMCISK%KpyD>^&+>N%K!ogc53})@C2D zPUDs1ovasR$2!-?YmRB$`o_RrUcc1(wz&a{u}i0KrT?{z(zUqYXLxVf{D;iw|z$Jc=G#TYyZpp)6Ym=d;?HCK4O)_m7e~K5`ojYWpX`b_B7^ z7TJ1$2-l66>Up#<`Cw@YGt=+Ax36@i5bZcr`cZ1ZV0VD?jC%u&{iz4g=_Xjswf*hv zjQt6eU|5)r-fG}wfcVe3SK_~UJ$Cl-mXVIW;PR!TToG~U=g&0>_s08WJUhs9&by5e zYfR81LoC44>ZQ0mbW*5JtJI%z{hmpzi!?dd$pSd}U^BXgCiiDr=bAb8={Jco-0k-={`G^RYR?b{CwuB# z6YCv@5BnM4Seefbj(3{Ez2x@G@wWZ|Rz}*0#gq5fA2LC``I@{_+446o^bdxoY)Tg~ z!@Up6hRm~|ipH9AcadXQI2x~m1E=`r0*$WU&uhw0jS$sH8L9BhnQ8&OCL1NDanNw- z%6EAnb?@IIW3uN{mYEpY9mKqAlAK>p-(Ci2s^bU>xlaWg+94dRYna^JQlz%F5aHT& zo|Mmr#@>*L)rEc;mmmBS&37W0*|a!2vf4@Zq_MVq{Jyt$XpahXIF5yAimi;$glETU z-=0yUIDpt-MP0ag`_mW)xnHv-W8cR!Rwb?l%=ML2q*#Og2Q?S>Nx(9vQfMAFJ7$y8 zk74$_f&oKwTm`>4M5v3{sKg$CECrtX3T4>EYGjwR?Y zujTjg!5YFq|M^owuenqxyHaAe+;OBt0jSAVZ_^$0WuBSwCI>84#K^MVoY?m~_>DCD zp~rQMn`nP3d&8l$kALf%vxH*#w$b}{9kl)MOGLpwnwl9FKMNhwc6fU&p!)-`vMKw^ z+h^mn6=ozmYI)zk7WEP;z>nF>#1a4|S{eH#%6ep5yaf8}^sk;1-5Xc~2*5LVks;ZS zklD6R7C33{n0S4_rEuJpy&)7})H5T8qn2sd8+*JvWl61Ggs!%D3=JaJI$KceY_VT; zUb5OD6(mPg;b!-RO7*X=8irX8AWT?|_2{8Je!Pjrx7d#&IPlt2bzi}Z9?=6)B=Mk^ z!RvPN+qc-20efGn5#fRcc^uq8II!|4KEY~~m4c^&jG!YQ;HVdR^GZpX`lWvkR;aYN z8QU_AL;2H-w4OK$x`zcR%nBig`1C?$gnJpW2gJ4z8&9xOvG0$jPJT_oxuz5KB8iOk z4vCc@mTqyU;q4UL`Xfni>=yQfUG!0l*H^fSTGo%Ru`iY@$RdLzmj`p#Aks9a@%lH# z0fLRy#k69Uw>FL4oE&cwDH#tw$ zZ+3}BVZ@a{D~td&t+IC(6UbTfhCutdC*xo9;5Hk3h7ZGN@ctm?Wsa3dm!V?X(^CBHeGC63`1IFKgH3mPEO6lCb{+9R%Uvp%1UZ1$p$NVW#b;@~l9yWh( zJ|iV2)#snqR>ivuHocx@`2=|5>5>!dJS|dv!w>w^oHD0sk~)WqFfuZ3Yw$07#@DT6 zZ>*D0itec}ROel_sGjrI9>oeb;C)hQC*+dxf4BMjCv94S^trzn`r?dVszka2WSa>T zn3)UCxKXxSm{CAdHqr1Vk{z8Wvrs8f(K&kF4(1G!K_z%y82oxIVqHN|wVikpIcjO- zB#$=+qutwKY;HJ4GQ!(+lnC_<0u$z9@40yb_?kG^MlhYu#6DznT4H`O@5VYYJFev_ld+vv-lJOMd{TU=LCiOzJJ{@AJ28cX+ap|7AK8JDoU+g^)@# zByy92E-IbuKC%kWX6KL4_aIUx4+Opc0cz%ZbkA1pGictf%uaU``4@+co=-Vh_;>WD zLx&2;P82)(ltt!$O^wQ{4zn>+2incqd`9JtH0TZqb^QFJJ&xk_j!5RqGSz}oV@XUw{M zmHQhk8>foNvC_J^g zX`<|?DqHMbuEqFW+eVS!NEmv)!)RG5K5lRrQK<9Gi6Vsmb}IrYt7&b)<*LZ_g=a%l zUeqDFisG5aNa@Ei8C!bGcJai%{7?>F)Y6szv`?Gupkj!eLtogv=sI_>Ejtl~{dls} za%)n^gnHNfhedaR(t#BA$zuBPPti9UnE)NV*ke0l7QuAmhW*gN8d53iw2B8Z_IB?K zU%SbS$9(KM3};e0TCwr_rM{bA#)E@ztcjy)k9Qcv$<^wv6Zm4=x_n8NpfuHQJ&%R; zyU-7>>EN;3o!(44@6r-ka&avUx+T<`@7izJ+E@45AdEEv^eu@7g4`@la+6Mfyb>}Q zz^eAn%W?#$o(pyvKUW+|%CDX$LsrZPUS(Ul^;OLebzwhgiC_ID{wmcjbSiK=z!NPW zx>E0W%9p2t)01U7r+%@4g{i7{Y)&mB6Sj2o*G2%ZEwPV%7~98sgbM;)JWk65{>SY9 zbA)0C=7BZ|u9VOq~V>-6f~Zwvh(Z1=`YM>ov8#hf9&8m}%CO8dF80q^rt zxXSLwI;&`iX-w?LclbiHZklA2q;?fO&-w>c1mb8LfH#T4es<}UyH+$DiKgw(`VDR; zTEa3MVJjMrB_rDba@)UY>m7%sUVj{Xxjg92Ys<61de0Hks^5-!Izd*PtOqv;#*-DU z?DZ(ydq`!P{raAMYQcT*E+MIUN+ARRi7wI`F0tirw@l8XQO}7JOn1CHkkV5lWzWB<&LKN0snrVY7B^~6oQ-wA@rupN?n22#(}BRei|3(se7Gh={+UdSCC@Wu74h5c&a{)p7pQS8xnot3Yi5wvkIYC%E1xs{U-F=m zLRQ04@mp;~&le?Xzsfe^qTn7uH3e;>zGEk3FWIGrg7;)F&D+JVeHnebIDK1I=!$tyxGELQjYDxs zdTLa6BwfSN&hLXXbpz=wBGJzL!;0$hU8BN0E+C3tq416i;|S>=nR_RP4fbSKrC=Om zoKN{fBv+U7ueQ!Q)Rx7WTe0H0rO92a$V?V3vb@~mVP~|*y9XnCzM@?vskLC~Zo>HS9d!v)^VkwQ*l-vwXxGZMM(I9Hw2ZeRv&y;f^_G`s$*RW6 z?m3wyUoYJxO2qKN$YnIHqZdiULdC*ATVt{DYtR=pMJ*=CC3v^dqWglD5$sC|HR)I0&|vPcTwFn*LTuJ zP4ofl5tEFy=PmY5nc1KcRGu}?Y84YQV=u70zVgW4?C^F4O7N}2uu_?7yLQ|g(r?k! zCq2g^+etMAtHH)G#G2aff8`yo!Ch<``W*OkE^b#wrZexl7;3V($SoaS%8`<3jk5tE z!;qe`bo6Zq)q)c39bU&TdS$_kGPCVwgm8QOWZ2BJ*yf=vjeXwk@A3TuzCV1A_i?;`c)yS9JkRUAuJgQJ z=XD(yPkC*~z>mHE{q7qeyNtoYRPWP;Lv*a1qP|tiu+AfYn1JLk{x|4H8&9#FEA#OW zn?ks6NXXQ&o{o|HUOmZZP=rjP;vf{pPiT@}zx7tAf96^Qlk8H1z#!O@c@t)Z`3o{; zdP58RJM+%!e`yN9EWXTgFX(F!vV}_`h&OZzL<30^C4aK*N0c(#Px8FJfwhM1s#C`w zdG{53V?@!i>?XXLHw~MYM*zckEFSBUUmI}mJ3 zOn`r?9}rXj^AN(do)Qxk>e(0JJ!&nrTG@v-CEYtt%`3`i?l{xSL21!j(bUM5YH_rT z@9yu4e|QHrzlYY!C-;JeKttM!R#KC$HC9cz0UVMjl9l-@rH*10%D-oj()hmv4NJRdvsZz~PyjZuG39LR>$fLGoEO)MLgGJqyEZIRC) z1ka798w)OKfa{`w&k<>TQ~Tm{v$ZXtx9(^{M0$-X@3y5^GkL~qLyvX1^@b49!!^70 z!MFDr%PEK&Jw6n{zC3@feSU1`ZDTXbeD7wJcW;pJKN;%=xy!*IYIgsF*>5HZrb)kE zyGu8IKW~xnxV*lgjTxB?!*QA5dhHG%)~|5m5MI~Sn&naZPVMI#-GgGB6M}8FL!Iht zyp(y7yz(#hzIvPG+A4n*-fJ#Q1IcFW1v0F4QTFbze$gNZ(4^&43cgWdjmIQ-tGB!f za@L?RgRHBbe*7#vUiN^XXx47V?MAjVPc3|Hn20~qs7CQ=eU_a+dWSXqx>k6iw61o? zXV}~^i$suQ2UGV2VDeF*tk?`3Zf>JkuD4&khD?|JBm z;?HVmjnSvD8heCuwX6b72OZsW*wK|+PS|(LIjj9e?^gE2q|+;%)^9pb7v2|yiyMJq z#J(u z#9M5^K95rgm&$81VP$-n!woeuUw)c>HT5?r*QD(0laL!xJ%`;@gB7YA;#uKY39y~Lx))KtaCh5#e!6K5>Is%t-&V?i>R_o*j`m}NhNBBF&vvmgKf!uuj zMdpYym2rVn2Y=CtYDsLpe){zZ2;qh-6h{=lwxpFQ2&jAaKiSxA$hX=3qB2zLnZR1* zp-AROTckb=@bZp>J|Dr@W+{k;ZNi{_VY=TBzqGM>rt~J}*UD#4-ISwiMFRotA4cxYcf*R#p`Ey;seJlS%Q6?}9IfA+|eIVgEE$A#YtEGsI_kbPP;s7E~#PVl98_2Rp^pO_+lj+GK{a32csG#U|Y!e2#y zG*2Ou-bAA_V0tYYEw)9;*lV7Zt~HVl-rmvEZZ@VWzxueFCth*RHW+xs|Lp9Av`2+G z7->zDnO2b)YEj!H=1~nf0@-TO-v8IK}mIzV1B>c>4TYVDB>Hq?(fD?_q9y$*v`f0|LP> zw+3TZKB7I$EX*&b>8jInP46;r(VYc2_8zip)vUol*iwWRvSQnV!hXk%F#1DXD5jr> z3+wtQ*S@@6+aew@+FIX{7jQ|ZTkEq?=|U3-|C^)I#;HO)i042lUWC?JN*?h=m;9I& zQ;Aimv#_L5(!Mg{8`M6a zJzXv98nL!GZwl{mVMcE1ip~p5oaZex_*?4cFzVW1)5KPu2cJ5s5J(ZJZ+NTZ(TYy( zvjCnlBV)TFz@kjR#Vw$B}s1T_{{mV$NiSez7WDy z9DS7Y5k1p&Y~d0rmo+!szX0hIjEZDtY~=DKnKQvMt8M6>lNI<{#0;u5G2=0Dgw0yS11^#uu)Rf6xh@U*Z zU=nv>C%&Lurdz=^LcHiE&t~{GfyEaJkHUiRf$}{kpGA#mUVJ(Gq+z!n8yA1nP>m)@ z7EX&46&anO4vBZ50q(l18BUTe$3fYO90pE*L391R{Po+uj#BVv1 z@qFl%$p`tFt~ue;wu{R)UFS*9)*?CWIf*tOoc3;zOG)-cT4Mb-vOyvs)h@n+VBO3mYS2`hWE*;*Vo1pDGkaMdz>k0Eou0W?JVji}|yK@t_LaXJ?t znd{6}4hMR!g5d0{g{#PW11Y$vw&oK4;XLkS{qgF?pfk?*?jCCR5gBv3ad_&JK24AM zRxKM%3K=Qr&)=^~?~PK^6`2U{;CYXECP}7AgRQg*N@A;T6Z72-obE{P5*C>NdcOTf z&NhW1SoAOAORqkuQ5`C!EPr?;RV`ZS)^7Z3(z+IYNtWgKO^a4hW_Qx_>Ob|z8wEOc ztj0DbUUFUzE%{`Z^ay}9ZAAbi1On^*qKRPl6g@kC3ICh;hf^V5KjWQTsuEE>H|Er& zEB}`ioXkGFsYx6E58FH(Wc~95#P9ByR-LD+Rjrz8sd^p%MBBmfto#ROhe|j4-Eda2 z)qm&$iyetGjOihdaVrNdx5#agm@_4~G`r->>AGXPUIl#z9J(h`imHih*_Nl7I-2^F z8!s?R%W0`krHM_xrg}j90LBaH-D#ErfZw{X=6*ajeSD`Jc@xz^J%AzP5r?AEV0`P; zfP1+y!WIlA&x5`6*Zkb6dPWWCC(s(=SDbT!uU|MD@L=#DHZNjU$?z{jjdE}mDrn9{ zEgc1PeX&t~Pjp^I?=U@F(k4fi?TVkO`{25uDC6smYg&7-Jcg+{w%<6V$6j{~#PF~< z2T}X!2Jy?G^8!ndaB}&*<_(QIfytW<=OBLdjze$n%b9N58mrSt=T@?~w|Sx8zc&7e zd~S)kRv%ApihYx)%u?EtA~>NxDwMeTYWXHKC}r50VNxPj+9Ksv9&Ljw64J@HxI(ex zez0k9f>;#0TnWZuJ@Bbq;r0K`ZOohE#q^t3aK&uka#ro~iVN=XFRJ%kz~t~&Z0mL> zrmN-Xou!(zfRDWHzMH2ILZ)qZtgh(9+8~_;00mIynp!nHbJ7bNua#WxqsH8dhkKYL z^)G0|K%YwW0LdDdn#Ic$ugF5wr`5>-BO-$jzNWUYbd1Q_yk^jC{{*jsTr%LTAHB~! zmu^nCaP&KJ*~_}_vz+em+4(xs}EQ3H(#Tl0=7Ls8?%0m*V_ z20@-==`ERCL&5r2OaJq44*G(^YD}Z>wEJ{@upoNsTzl$WO*RQnz_PE#IOpaxH|Hr4 zJ>0p&Ax^+tnf`G76|L#a=x$5HFOmc^7vJRLRD6xnP-#vFapld!4cAC1g%7?*z_%CS z-x**cy!s2SNHv>{yv4b3dirQb4&Z>q(ugEPGD77M6R4C9sM!#}szDa7jL~uBGoqeN zh>LdRWy_+(WR!R#H!WB8az=#TvHJMsQFl16Ri5W!TfqUxK+TsM zzM^Px6&4AXmTI*BF5!Z)s%BC%=gy8>L0}9A6nqjgCsVzHj=iz zR{+NDz{3RjY{c@W>)Es8joNC>rr1kNSRy$X)kqnRV94z2&m1w8f)Eb5I92NY$H%f< zvSmOn9__q&%34syE@0M;g?bvjIICjkfLLl!t|;B3-f{?|G|`5R;Sl!?XErF1;dIb= zvI>%Fpm!ZKoAo8mrzmefL8B)~;Wv`kYc)-h0OwIzFnW~eo#+hC zBfs~&-*?V+zVn^yJoAT{=b63sUS+R!uX~%18fveJ@agbDAP~_T#aCJ&5PByFgr+r^_EIr?4PwCh0TT#@$sfs zQR@5wu`%>gzZb32j9(G=fH8Q-(vrqT@<=2__hN91rNoM^yd|;U?bf8H!3!G+I>KVZ zElhZX^@&{eZPI$~A~Uu9o_lqh=a;-)!SkEl1KY|2qdJe=jfLZM=_-fZG!ZmlRsXx| zp}0G=ZCm>*GM}yidk-Nwr!d!t2Mn^)YTKq-dz$1pXMbI?uvk_>jse=#3W1gml+mDN zrEv1>Q3y_OH|%bM@mN>Zoc!z$Fay!yE0I@ZCT`&g@&+$uqy_x z{Tt>1^P430sT^$z0cktwbRnB?{J@#EUWWb$+vZi zZQ7>A?RKWt)m&OIC*=v2wThvv8#a6s>#*F$7fQ(Xp1zx@LC7Iq5pn2$zMeROr>{8D>wlI(JT8{w?Qz~>xrlGk z>5V~4UrEC>JI9|T8qgK4q%9FYV4*hF;frIpHLZ-kb@ut)z-|+DUK1@&RbhxVa@l5R zow$e{gEs9g!=-Kh_uwqv7Z<#~s5m!CJjTA=I6$?fwpL#2>-oUAe_ zStq{}Gz4;>Lz=H~%$pm(5Zr0Ilg-KLLlfQk&Gw-Ue=;0h{{UICDMRBF?xV$p|DN<@ z^s-GY<_SZvRB^Vx9R?(3w%jDazclugJNBy?k@5MQy}}$ck9=oOS7?hSnAU$qYJ)i& zy$j?Zo|2VY@A2U(x$Y`OWfbIal~Q+Q(Lyya!|p@*g&;o<*39vkKFas6mwY#iXp#Tc zUZZB%D#x1AMCF2udBU>oM7vGB`j$|&7Wob7(4ldwCPD>^n6Q(ub6sJrgsqY>_xFHd zR%|z@^^KT)BmSh?T7|xDj1mR4;v&&*z#KD2ntm4l$B3mDt$rs=U7`vS)g~WZ8ys`I z8@_pr&I%X$DOF##ia$MLNndWXD{An=1Ytw!t`?+tNb-$ES3ij5gL@~&A@-V* zU?J3T&Sp)i)bZOHZ3bkH2ynn50WZi6AG1_C)Q@cbdcw+-d_AVR8JD|@oz#{y=>zU< zU;zHBC3T78Yi(;K2^KXofrdEFF>dZ)%L^>(E?#E|uTM%v-kpnmVEgCqhHh)#i#N&uA~1L{cpGI_gzetF z#I;O=>hua;<(16P8I{v8g_2!3{@mA2{D7NV4JN)6wp(w&V>0)v@?2cvcF#guSJOI{ z`zcj;(vx&U{v(K_dr5}oodo_R)+rfc+Gk*_es)2=~5+&xap+f>}2=>yH7v+~_&F@`boq~Nb_IYmI_p;*d zbHu9*JEkyH;s6lkZS@}0x8A{0U`bP}z{=X{Ha*{fN-lPK{}pB+xVwttML0bv#Vf>h zzuS~a4Culn8f;+RDRq6iuRjJzfgN-9b7bwR?X$YY9UN^>;m@ob;w517k6+kbThRm- z`3gcCI+=w@%B{5-g3sq>3$CMGpLoCdUF}r6-#BG*RBvPAK}BapK|J!6#=i?>aw>NS z3*hs!x;uUG2FI~7UdYSyxwnE4OzCZnP>ztr0d30Kf>>9t_qT=^`j$`g5L_mGV;w!!$M$ov*FDvhj87i!EHYXI?<4#kfpk9n8&~uv zB%AJ;D|NX);*EQ`aQPf~r_nKcDzK7PVu=!=d7~jw7GBA=hlpuV6%71D+yRn1+|X&& zsP>+Sbj73BlxWbS1W-xfQ{;}s^CGJT$Lc-$C%}z-wMu){j94ffS>n;ktFYIYP!;LViT?jN3fA6~GWE(gd0R0JbvmC?pAMDj}Jgvgzd)@vPf90zOqi85nSy{>a4x8LzcpvE%+1mvN!^KTJ{^NL1Y|q zW|Rj2opS9uIz8^{uR5E6aDHeNQLPx_8}{#UohYL+qc%PGgSqv`b8NQuLkJdg`VM&|&=&`ujjmoXWkUGU-c7;1L?Ebc$N;<-njzj3K40gUYgj&_y*KG~HcK~G zf;=;{vA5C_3vVy5r(xJL|>^VXP0CI4@Nj1bwdx@jg7N+}G zZzV>J>M|acy-%oeGR9EN(m>Tymof*Vb6AoNTj82c$6FBdg^kb{$8pFNwolJPS` zd;9w7OjR<%F3yQ~zzXGD9OX|F5sv+BLg5a36B)%!N5p3};@Z6-Xwx`^&qR9bv9&gP z2E4Wq6NdYD{+z#98yqtoG$N9Z#q&R17P*3};qF0s$8-6v!muih4VZ^}!M4^MGToIz z&}PnKrhTtr65sfR?LmrW!4E1L|2$1bxCvuJwG`%t4jd)2)+$+Q(GA+gxG>})j-zBk z*JdrM(#8RRD2`)Z^e59w@4`+TLHg^>fqFO6|SXq$a) zV3&QaaePv|f6__M&}TG$g8HJ;z%((ge2I z88K#!PDJ1rt+~ zQ6F*&iH)>!teNV&Q4e;EN%yNjUz}5Oj=8Il6;NNB;77tI;RHyb+rzAyo`%V=29NSK z{gH(in5xikFZK##bd^Fp#lJ;DKkZT9ltXL1dnyOKOzq9Gs&n;5cI4^fX#>ltFb-|n z@0K1l!K&VR=^@H3W*zD8#t#kIuP{;kx>W?NLbvw$dAHn}wlL(`om-mcY)|qRM2o{7 z@(iquenr_dbuCvdm-2#Zc?0`sS~+MB{V9;I*B@29Rb7w(=dKC2yZMt&sh)Qv=& z4mYEUpiap$GGb-w3`fsHHI#p>zs|#)GpcP_c##^V+dS!Q?h0MA5%g*0`TlVh*NW%r!Gw{Jq2WQu_$eQ%kokOn?Xn~J zxP{~3+xd+(%EDIw!-Wb0T42~ZzvEsjna<&O(5ZH(J-?dAWMBE=Qr%rbHDyP~ImVHL zk`^J&rO5@2F)DS^a}xi8n40kcYCn$#t&sLTrbJp@gxs83Gstzg>X{vOY_{F8-Bol3 ztT(H-nMb%Z6`BpD@yo;&>DnF!;|Y|a^&bFVE<8xe#msC$dw!RlG2Ae5jwVE9$; zPX5jZ(b*u?(5aeerloqTTV#Evnqm$j#%vORKLXpIQY}2j^m~egA(UIzcQLMyCLkJ#!k5A(R z#_pusmfNtZp_FC1cNHeVG=W_phAMCOS3omYD+DJ4aM+_J=W+we7k)>^+6+I};m!{v zuzvA^M$@G@up!k`$R7=+dJ+eIH)8JDo@%dyMX}HcJ8OOUlk?5Wp}n9X$Db`a&(~o) z$bIyH6MwlUr(SEVXRvacGa1rsbaVd^c2e-do^dUS9CGDetEA(y_-Lj?OMbt-MAsY2c|GJQwJiPOt!r*Sg-CxwnaXijT=KMPa z@#~^k*R2N-DG0k!=GafQIrOa=@KtvS{FxTh-rcWMqG@-IT?-W1bNQEd{*7*5nu%sF z&rT`uvq_HW-J|_(ID01}&tzx_I{^gR-l4;7I7=OQsoD91UY}XB_V2-YheFDZpmWTe zj`Ro8@azNAx-p$$lI|wsw&m*qDldf-tXs1-e@#jJ3^baT_%r8DYl+2vE0|UexRM6N za)+u5wWth^DJAC^V?Y4qmkBs^a-Fd5PEkC`YEI8Pge%w#QO%_zH}GUr-aV5XdkG38 za-y!I4_U5w*A!F&kY-Ocms%!W5%^d#VOQR1?1~@U#Mav$MM9Nmlx3R=6PSRy2W@Mq zv+I9!sN^>GL9|Vg`y9-6s=3i~|3$M_U3g1OPm$d)w(Ms&OFzqV)loB*2G=LZrhYlq ze(ShKegZeDB*btLvH&%(h%yP`Nnrs{wvRZySa>?{tM;BY4?GX{we>F9r;JXyi*z>t z_e@5&%6);3Ue|8DEZeBW2x;VA(9-v{Q|fJmVe0vxP4(xShXRqX-5 z=T{+~A_Zhf%59#nIQZFL{}N;8;Zu_=l@6q2oS{*7=@t)a^I~?4c9g!$z?dUH&5Wx1%S9D8eD_2t}P*YAq{58JKCnjbTB?Ey z4vHo)=<~jA)lfYN(99sO;u1)@>L!w~uv=a~6>YpL+O9=EC-(;<%l@kZPKa%r3O9k1 z4&O(XIxMOg?=TpDHMHK7kkcx??R^p6p*hNK+a}h<)^U6IB7a-gMNPk&Gi_=GN{n$R zi23S+j0){r=EH~4aE~x?@*~n#Fyk{{>e51>tk_RuuV`57gbb7wL&JzF_O@k_#SQ9z z_em`q9MksZoa`%W5N>B+TB^(vJSJa3bydCBL5Of2?um{wCSC4oIcTvG88+l$5DB}! zjea{GVdsT(5X%KIRa6-K_021ZQYQU8tzMJQxmxPe4HDa;R4qpC(aD7adn6WI z+qBF{@Bb0CoG~FdbV}!TH}D0`R3Dc&z>d&+4n9504RA8820>z6txNnm*-7I$kHeSl zhI$36{+QV2qQ$@7klQOxW0;@2pNG%g^NS83K)U-!> z!GDX7f{iWb>SrYf9bzh=xV|Oc<=%?T{5TN(LNcUfh2v9b zAztNtR@3H!7DG<~!0M2dO)H$^A5|O=4 zz8|lLfaVg_#%zC}cgR9&xhEnfz{VDHg0qsx4ly)HSdwnqY{e~jSijv|2Ej&M>=RWD+!&UUZ#Q6VT z9z1mU-!q~L_tYv(Ujhmw@E91AF#+%qS5J-T*@pY8Q@|0%2*clJMjehTt=JC3u%@^8 zpumi!=QJy^uDIj>#bQPcz&~0!&b@PvVKL4EkT}4SU_&rj|F5*9CFsE|7HJG~xraZC z0ct)^NEMB+6Ls4&1mkg;?a@Q#Cqiq4P~$Ayw1Jn@($BL~+GWzw($Vg->N)d&13tc6 zj08#y7kum`IBh6xAT3aQhCtc47%s{GcaBe*50nSr=A0k6z*(Xs>b@t1AV4+b^=>0r z5Z7U+*HuU<>iNoBCGwpa#laMKm!Ey=hLF|;c#7F;f@ilfypzEEH2;Qdw?>#aWUQWF zFe4x7#&Rw6@Nd%u_KXz}bK2f04r^+k+ML$7PNuiqkNWf9qb7aMFNaf)ByMwz%Aa!H zQ)dzwE(VhMCScFq-{y3j#CM4)LIIuG^r!cQxVX4*nzpv~IQKzQW4X#HQSidUA;5Tqyr60zL#63+VjOCQ z%dA!Gv-`)LtN!uyYjmA_h~biMQ+B{<%P4lansku6dpI{gkMdlgiX(xwz;7Q0K`D7yb}Fh54n%p-m}Sd;k1E0$b2SkwdTys zOgT=yip?y$3)6PrOA-EzA-jd6+bnN_QgmL>Ya$59>1f29K`T!?#@e^$FV$MBkls5o z1{%QSa&EG>b+z{P_7VD(M(#ef%Z&)cY5D9rqSew5%%%J84<#-(BoOUGZi{XVhkm}? zwi7~u$}Rhf&AB5$Pi!9@O)Uu59b`jlP;d*&c6D1HnlW<#ZQCLPYeE&_$(%pIr({A?*NlIY8-FuGi5q1w?)k5Xee|{G`VOG8*kcI}$$5Zk z{THy#+AmU$uW%3sNcedf^E3VYw28{?qJ;n(Cf&SM$xc$IkUkJY1SW=y$p=y(BJUd^ z+wu-A)`7KCm5(?yIq>#B5~}qYJaEslhrFN=9CKcf0OM8V`3rousmMsI#!$79Y3=VUoWr`j(IbxhJ~$4G ztq-jMiV{1U3IiK5ZNj&=jRfjYlgtz35(!YLPRZ_;1VAD}LVHFH4l{RRI~8^9uIm}L zPH!iR;^^t=15BD%aA*S2?zIm$bAP`&p%L^o@BM#{JG4pM;FQ=9|5^-`8xS`FAORG- z$W|VpJxv@n4TTc!4_8P!8azJ#-1@yxV&H?krUC(E0UjU9=(YZc6l1q|;T7^JvU0Y< z$)|F$;T;&G!DcmHGY#IMO9D%qIEI_~nO17wXEYs9Pqp~fwr(7qCbu~&ynhlX0RrFU z$wQZ?GR0KkQ$7X?KOE-3bBzu_atG2@2ec1ab_!SD(+h!4)74L1InX;2fPSEXX#A-_ zIwT-K93bKU_a)800gnLAb%r^_0U4b@GeFo?h67AJl|%xZHz6_ZS7AUCjFwN=)UBHm zeU+G^c@&gYAbOij)QJ}kX}@ET0%0$0PL%m1*X`F%6{2GY4txV;LvYuxM~_~A>RR{N zeia7o0kulsOiOwA>>211K_^_@6U60)KcO~6x2BRhnEROv<=)_A;)XAgws(yDxhU#` ztW4Cj=pi|DFtC>>km@O%ztkPG>dSP~%6Q6gCTcwABPJ}jjklpfrJ48s#9qRb#@So~DJgnS3h7ltQi<+IEQbB$=lap)_k!;Z`EL&yfC#)~Ncaz)izf*SfJ z_Mno$?s36KluJDm$#9rE))|V`G}C+zV1m7$gTX z9JlPz7ab$(26Xey-QNsgFX>?~;UP*qKx$yS#5-RBP$NGOQPkXw)IR{OJ3CKrW#Ea! z{AA>tC~x^@Ja4cB%wOuGBMH1tTM!fn&(dMUsNvHpbDM zo!%782z%)nFX%6hwBC(T^P@e@1Aty@S3syqXjm-T>S`-tLu_xfat1-qA8o+R9xTS8 zi8A2%x6=l)-Co{n9=rcY3>%_`2JE*YCY>tjYPfp2k*<=0pW+_RmytlY`XI<5ro6iB zQuifjRNqW{WkCKVD2^Y*c6X_^2%IJSTZm{&)GAG2Gj2561@{{MiNGlvoPhv&$Ma>O z@fSbHN1|xKGz{bL>)vz@Ge(V>3t)uO0Fc@lpve%30V(fF*g204jJ+A4+QdRuBpMXv z@_gBE5hUsad&0wXc|klUcXk#awGGg&dzw-D8QSz6uy!@Jdsj1Ic7a|3#2X8;q~wo@ zalhB^jhn*DKyOc=;O&SZQ~sbe4aSZYJAjxhL29?hjCY`H)W(bU6`WFOULg4aq2dW- z(sCV^{&GiX9%2~GxxOOL9g8~H#(_?VCjeId&x_{1p=S6Z?jGklDRE}xKohuky?JUd zw%mA#<#eq~*d?_67C4h}R0~?VuM_|NxX=q?FVR>{q*X+JBEdNP^Jqf?T!ueWVv^86 zMdN!H(xTgzZ}L&kjck6b^Zxh_m_ptKX!Zy@JE@b8XG_;%vS(g%SJ!RJImHd9-jQ?p z@968L8O%ZJ!gll6TsI#m`7wA8OSjbyBuA$>-$;V8klSWJgbxDwA2`4Ot;^87#ATHv z?7WqCD(eQEB@4T7K{@MYA;-751|Cacbx*+6Q=1}A;xl`T;=S&r#mTN~ekTK3uBdsf zNHo!|Fb%$y3xV#au=8cbykIc`coSID>XsS46ydlx4Sf3FT~|C`SNEiDkIzvr zd^XS@=r->kkJ#qMmd9GjAQS2Umgh7*3@oho z*9ynuQ1w5XrLyF5xdZAMys;i5nQyKi7;Y}nSC21yE@C(JNko&aL%5+DbLs9y)g8|t zueS{TT{&n8at+4T82kL@y~BFI*hjHt&=j!tZiXTGm@ifOZ0zh?(A4>7;qS@A=$6-O z=E1|R^u6(Fg&M4xkNNqg5W>#;^CJyAP6CVEQ7id`urISavX~Wyfc%zMl7gCLodL|vw{_kxC0$M3 zE|c&ip?>Y#10}HxS+i9CH_rRUDk0<{-_N7;3}054|IA3_32PSBvdWI^h=b#s6%q8> z!%8d&klBhQ1W)WQ7c9gxL<2vu`tkGX+sP~3Ur*odMsj~FdTr&zD3$q_wY%)Y7P;xY zJ))SpA%)Hkb8FZ1b5>66H*~X7w^CbEm(|-a! z2ebU)gj(T7W5KRw7d$M{c`k0k&I`N!p@OS-GV|}c9dhn?o9rXYP-tC-dNCvep*HN;p`iU3@F>4 z6z`gnVQ(L-a0<#k_Kkh@spjvd@u;b2ed6W^U%wif00pVLpLO^`Fsj&u zf7cs25m(5sX-%qzE$y<+oL-$)hmR~8fGW~F1p--4P!X5$VSQbPfD5p=q!018Nm7^$ zmv?^JVd1yFz6Q&n7%N!9tHLxph$QN;$K!cTLi|i$Ak!uieRZmvv@RFVr3i)1yF+X7 zC^X2SWu>LYRKi=@i71F}0zbz0$D^-nsm7aW)`Oc9rFq+he~aSm?9cZ@jS6O_1$QQy zol13|lyB}>nvGvs?(F$$pT%gmi=R>b&T^6Zh$wL>Ad7V9Eis0zAMzLeQg_$XMv>)2 z@2J73!XL4+)+O*oJ)oy$?vP zpzH}HnEd>Eb&ld#Cv2^;mV9^Z?~6zloImN4PVtMADhzVOgyiDnb89wkv%l9la2@5O zxx7Ua%-oVmZR3^$?jCUQe;XYyrGSpf!Hzphsw{H0=949~?YzcZjWp}KN3P`$rv8K! zlI&JIH+pk7d&l!9+qj$7}eFHhbQBt291H>&5sM|D_n#Sm~9F40lian52sUT)G_FpX?f?lkenfO zXdYQ^ETewXM>l+h;|!PR&?`?-Qc`-x!!w$)YC*hSs34&izJMKYs8^Dx&++V8ex-3^ z3%Tk2Zi4j>M5A~|ENU;p!^3Y!C(5+*sJMg0(xWmL4MR7|9*d?m7oh$1!AL4kT=bH) zt&lhXj@Qp`#s5Z`h<<>jVy@7glOiySaJ=!J?1bSiJjT5F#;`XP=kJetCzp(cMFt(i zXu}Nlb)vysY3j)SezK4oT&5BjCy4X#AfluH37 zn|N}-lgK|A75=Zp;^-}3lNLOX_^G`4DLR@^%$!8v6{?+202#hUEd)zRc$ZK~(I56% zEkimYtyTWXU>d6L)A{E6eJ62R=JNS*Y&TwNq1f{=iniKtjVB|%K8kBFZbo2v zbUhN!vo5Mz^xe>U&xhltx$=QoVvOD%H|B%U_w1ZA4HFX;rR@|*G`mbpotUPqEpRVB zFHik{+-tdGyg<=dK)0`lfRaa8P=c3>Ej4OlrML`oi$J?<3lZy#@0MTpGJiO^aBlGF z3PnY}qw}5$&%=AP7F&YcXOV&>*_SocI_puVJfrx}T1h|H5J-N?OdTdqvR`~6Z#j+*@Ph)({>+;e8M38S z;#ahO(Y2@EvRvLv(}D$@{QUf%U5#l; z=tc?z!@c&pe|+XM0_=WKA|_<*_I4&&yJ+gFqv%dWy)B6f$kg#^M-JVeHu$#lASWhZ zj}46d`KX0Pyv>PnHc*L~r#n+cjY`3YHEO_|6=@4@!LMT(F7YM%uO!{(-LgY6pU#p4 zrW%d!)+{L{zTEr4V6C|PQlRe#3Cp#G4`wGq<4N5PSYJ8E2 z++_R&3JIw+*50aY)Y@`vL@MLCZ4PkmxdS`0dtsWARrs3p@q_xAU*Ro|I|{$GTeQz7 z_9s{o5jnYK5_|JW-Xx}ZzHM#9K!jlzVA@{i{)XZ5PXEe$cB)sHJZF;hp%{4fQ@M$a zWyq-{oF$Boikh)$Ng9E%!3fiOr?yO(^y4LWrl&ZsGyfW)P`qpL>(|SETZ5kkR&9Qz zOAadqWhE)+ zv^UpZHobpoMdgiT>uTB%#Sv2TF(;~cqH|OqzbyY?eCzK3hin>33(d z^~ZAfw&~{GZ*+V@#=|&hNp27(2?iVF6_Gr__VeSJabQ_IddLqj4jT5gpYNuIG>FlL zR`OQ^7Zv9c-8u2!X*9D%BccD;`n}3GPV(}lq$Rlkr3(rKqM`U~@@%4OHc0xl{p;?< zYQ?5t6gdit#67tf(RrM_ghfql!BQK0L28J>{lmq=_;ws^zQ-Wgxsnd3EMpghmfz(F z$%jRp`BoqN9K0?}HSRiAj^ls#La;{Rjb%)}Jp_P`t@RGXQ#xgAFk3@pNBuSq;!He2>JW zPcRD<3XOW^?8eGU?x5k1XD@gra9)A(1_uf-Gi?f^Dex4m`lNvDYZrS(f!M1NIpZNe z8lYI}BVP$n(GEm@%%N9@PQ`2Vb_CRL+%T(+cj zAw62LhU`-J*~GgmIhM!B|5`HLb%u|gzeEStqrC(cr#bW>FmXps>`2fzVRFEV=FYnv zGT&Nc0#|%L`t;g41@XpyZIsSF$_M4$V&FbRWAD@oBs*cHhN!vW-i&;2VkryAOEf## z&(dkJ`1o|IZd<9w$e*vYo$20KPkxX{rr#YuUkjtgemotCLazuygmN%F;Ky}&BB$_V zIuI4C#E5Q!_eSV(7xpSOB&N@Qohj8U?hcn*ViRvLp*>FU>b1*e0@H~>sK%E!xPu-d zxV+w|%?s8hW)#<>$;KmehMm{*_Ca;bT|(&BaZ9ZRinxkssp#{+8l_=%W2XJNZ_rZT zWIZr4Y~5v4g!ADEKkNY6L%0g8>=!%qa3W-llEPS92#8na= z?{w3rbr=VqM=zi$tBz6TMMO=-fUnCbhU+xN?;Gik|4ib zlJ;JTW31q#HyzUm-kZ1>Byyz8KV&e-_+KjAdYYmWp4}Z#L&$M6>8C1zF6s}v(1yIj({aro&REL!5M##hKJS3ZRQ|+Ob})dG>b&>y5%)eI zY7w13g~}2^!)z??Txxq?NjQjT`~(YNt(jWXbmvQW&Ul^NH79xhH8B_ByR8dV_}9cd zlhNJM*VH{jTq)YWhRHYzP$zn*w4~wj20bN(plbyh-`|%d7`K7vQh4}2bX4;=908P?OOiqc= zk~2WWeQFlDbcDC}y~R#88IxyQkYe{eTO!IY!?5IYpj(LB@X4P7|3Dj#p^E^fk}dq6 z;}&h{zt*~s#-y^3sOVV8B}r#~Rij372byz77-&)eM=ndHz_Re=a5W zjY=6MWR#$Wa;fC^hW)C6ASCe)8|FG2CI*!3! z(Ld##9C@@5kcIn;WJ`c}?CzFTy+p=CCYo+^zl({9W&SN^>4LqTD351T%f1Mkw5>eN zC{jS7)!F}bIL@`TnI*=GD@jD_(z(s=S%9dS$h`VW7}jC9VEr!yU#Nyvn}ZsOs#tP<48!(p)#+~a2w z9UVLp&5dmC3kyLX)WpYCHwhc~AyNmUQYH!=yR8WvBsIHCdkUpWU;mym`W|3kd5v3# zilxsDb;LdFpjGO4wT{1@uMoiAxp3LnC|D9FauxSww9(KyQZ|GY%iyV{hr#V_Z#6Pu z^2-S&pmQQ*0=AJaG6`c{jEfoJ$-Q-Dd@GTg2xspwB9Ep!WdIrt*%wSyUp{~STyCf{ z$k6M4`FAZjA)z4EOck#WXe#;!e;RKRoS~Tw7aFZUt2*8o?WZtBprA^;KrbQ$4quS9 zMMp>9XArPaiC(v@ycz-^FL2o0)uE(Zh2YuT{P=7`<$Wd z@{vo9vafi@R6e3taRGddx!@e?CWB=N{b{&ZA0;0>4S_)R5e0S#bKUYQ90vQoS1VZ5 zpea#dBFpsgp^l9z3;qM)lChd`Md-^YdEmtJ|+9J)vXu zH^o~65{|t8qi+#4o)X@k849*7MJ7&}t@C`our1vb-=O~Q4hXp;EXNXp`I>+A=_C0b zbiCNmrWTA7a(@25J|nub0>Z`p!e3FsF(7526h8+7!{+tHpy`+Oo1#CRbJcPtztO!5 z*u3qlUI^DKdwfs+^ebb)czGUle=};GxfQ!HC+o2&dD|P~69? zMV`~%aYKbn>eiYfB|ErS?ZPCZ`JG+t6PLG`bM9>x!?FPn8$Kt+g+-9NncBNKq}eJ8If@@il(Yp;*}X@@t*%=&I7a6K+cJ9$^_M z)i`#werlaEc>j*Kc4G6G#VC$Z!-?@i02*MgJ)5;_bHbdyCxOnu|i9!~y+~auJG` zdms>{P@vExHbAmcNci2E68egO;GmY+u+b?e&qc1GiZYc#3Y=&LP>dAevXbg*)3j%e zwKKZEU*A4$JC7SJ`BDAg7slx7^a0v21?|LT*EjgtZ{Y~QII(|Mr>t?^Bx+6V$A|ZN ze&I-J=+<61;ySlM0I~rap?cw`)9Ek?+xoux%w3qeROynx^7DOE-Hu}d@my!A`610= z5H*yo&$_wfG4x;o-=jwa zYccP_RElL;_RR_1LbHnAsU0%34ii-i+Hg?ezaz0sXEg#p?6@zv%4yt0cDv(?ZT7uy zBdb}PY**P2VeHN3Br!9Z%C0i&xk|FcDY-=*(UmX!wePH;6iw_yF?Z)Fl$nBA`%T;q z2j5Cr8e7D!f1iiaO$aZalkwpQt$LR7=l0e+D=dcGLx=m|0xHrB0d?rELDR#4sgCFM z2bFgvJ<8PDd{WC2S6yHKn*%0-(jSM~j;^N3xv~w9jFQ~oFy@NI4cNTa=;b8XN;}j? zs{)n}WbG(APl@G(1oLG^%uB4mJElg9-cOVJURQH9rJXFJ)afC)GrGm8b3O`ln^x9B zEZ@!8_P*VSzu(T$K*E)yXN<8#8wQ8hqw|Aw6;>NLp2WnvN%a9dOiu{#g-x$_?Dw{OnOw%H7b06lajDV=v7*u88fZo14#C_=s^6+xy&< zAGRD+F8_J(S|L2r*T8$xBOVezV$1Dmw>iGpvxq-%IaK6ScP zHa`M(4g7MfKK8=?Q%-Ei3+B=MNl(Wwc;-T0Ss>%5 zO{j7{IMV{)+)Fxej@LNldM<<0c;LU6?B;K4m1*mnt~=BwTk^yyB)v3oX`V##VktZv zAWIJ6!9Fdy=u#4u51SSP)+Z8X_wUS$BG~pSY3G()j8{R{ogYK4u5VD$!;x9kdgYVYgB-m z79B--0dkIL>e*Z{<3gLPqrTXp{>#-S_hkonkHXvsw(UzKww{57m5J`_qd}M9^eOgG z9Btn(Zf&~!ZsJKtseWZa=75rH{BeWBHI4V|H|pTcTvL_%(Y!5)hb8P@ku|#U)8s zOu-Y`;;N-maql^PY$Cs*n{+X_%Zj^s5&S0D_D6FPPC%c}6&oS#M7XLe;JXYyfMf^{ zafdTpG{z?wKQzg{Hg1*qIp#{H&(v1kK*~^u?F)enr|T}2-P4or?DFMoHMK$x^*h%|2W znN3J2`Ty#E$IiWl7Tr)zBg^9>R7Z>3a7Hvv!{}t=3xBztfUVv|`B1s)eYbM!nyBHp z&RS@{9JxI?mwn5gb9HDrKLyOkGM}YGg6{&qXIk$08THrrH8;}&u0iLFQBPS}kA>v$ zaTpgSDZlIRu1do$SYJ5>)bM_*utg&#jqEDzkY41o>pTT7X==OnFwxcZa3k9K_c|#S z#R+rT=~q0Qw)0dR(M7_9mI!VhQ4IS=*{k2ti2114O(!%*?P~c*jw=Xtpp;6bOiim% zKy~ZB!L#@-hxu_mtC7Z)ViReS)TKd@wEV+z)Fh;Nfs&Rs1B+1X@X2^CyTLKP(f-o2sF93Lw$3zYXo+kE$hl#;zH*q<1RlEZlfR;y4Oxw zJsO-QrHL#!?d~*DdNI1*OYgEqM@nr&_p->qr-MERD`h-6;N#+wi+g$>8}aA-2d<5X z_BiHW?GQw=P?p>OOjC~ej_3Vwki8uKmYFMgrb6%h@`0*?LSNhru<5RK1Ma{XY^*DS zFQy`8xe6pum4Szo=;wlAUWgjJ7()7vLDct6#)gIGT@twig-+3?zL&EvUxuLLlxl2T zQ#eeWei9`&y-eO;(ut0I(Vs z9UVOvE8#{2ZDj{|=|;W|{?$;^s*&B~gf~X)E&C>11Nc*TP{NtKsNWwszl9X$o*Xy} zoib-o&7Xgaj+E@$;%f^pNj;}YHd;WKxvD@GUky5*8-`$wzU@>7OS*m__thj@v|Gze zt_7epQxwvIZ%x(fiReLdGntRJTNOQP}g;HkZGdulYgwM7zL` zmFSl)MS2!@87MG7fpTZt%uB}iKVwVVC8Pg;Kd@h->ji-rd`?{kBsFnCopEhngKD=c zjzvep_>E^0eigSS3ptk3Lf?L!)t&wQ#ep%V>O|fSi*lw4>s4y19^BNORo+Q6f7mGn z!D5C3}DbWK0zd)}?0(nCcOKx_-S{%1W zbMdTAWoX!k^hcCekc*h}={Shcwp+Dv7{Mdvj&(}yKIgF?*SM3olNW7_Pg>Jyx{fts zFV0_)dBj${u8-*>ta$aJCELT(A>2o8zHDnVP`50yRfL7le|*<{bXb|WlieVu#axIt$z!$2W`~L!#cBBpXuKS@Xrs?hMp635Y%Gn6>;q&INFDb$g-bV zpXkJI^!)*hch_h4ZIaFWMl_74`@oeUq5v<=f^HomCcJzH!4UnVsTpS$10jO=UG7O5 z#CY>*V~EBc`GE9FF~k_U=sM}TtPNyoy|$j7OyRy1?DVQ4$&WwEEjVLLx5Y^%K0LPP zBYYdv8FTUctmo=z!LGkrp$5AK`!H{LM!fI9N1Cp)pb;x|c_Skavp@BV%&(v=+}d}F zo~}Q@>@`83_yVfs(p4xPwlLo5dZDdmsmhCthmFm+cWAS_6w@O zMp&TNi4+YKDVpt@@>+o4g})W$X+J6_t3Wep!=~-Xa(QWq8j-_%3ySMwF%TYL>WX;p zj!U%kz6-qlywb@4y4M^Jc2`(AjI55RaD07c~rm4mQ3k`m6>R zP2NPj{!t6aq}bBv#mmSiZgdT_?$K!#WG^vTN(BRNj-n>~NtBlipbutng-7u}3r)VU z#~Px5Ub~QOZ;?;TSx?h1ULbKNK~><9{(}&g;@BltJ0vK!G=+CG>cS9Te;pV6kyo3p z^W)Kuj)nq>TK*h!2Rm!ioJ1pzwa%j^T<=L$2B+}Fof?3n%Z#o^7URUS#x3o>qn z%HbaViP^Oe#B9Ng9dunJ)*rCfUnZUU5-rWgEFw3nRck`2$1>~CS{buyL1`=^h@O|q z35I>RPOn|qi68+e;$YI(HP)ixKSY23AGKV2IMeU@-yG)9hAAv#l1h?H8Cy+?nm*K0 zgmT)_K@K^y@L}dyIrT|ekwaq2X&s5mPOO7aa;Pv&PWiCqv{?3g*Y~gA_5J&My|3%} zmGQl1H);{nn1#FM( zqvtbHEO_j5a;pD>a1b*3TzqLTbY^B;XL3J`1RFY(7J_`n4S1p-jMHr{wo9~8p2J^V zaZV~tDTuJ7>agO&V5ecEa&R^9ya=Hlebt=3VxeV}8|2etPMD2^f9K~!s-_T=UVjt4 zM{s@`G`}eFhsTD-wG)#Xpkr49HNLM?21dJVh*ag(lTT2zY}xgqVhSRMTe$P$zDEM~ zaeS#cd(tWNC+?e}Cr`N4HBr}XBJ7?0r!YZf#+s?C&-VITg?It<-6?cgv%j-mE;=8d zGo}j>#d<@I$a~0RCE_N?uZ8<<;_q)KL4(jLb}yQEb3b6xM(FJw$=U=ntjZpzR~}r@ zD>rLSC#VP1BLNPYnP;rJ_zo!B1Hc4CRKIfBXO$TO0$eD+=kXP=)O$en~%TUG(Z{v zQg=-XkNo_2RN|e!dg{7RT!5o~$Er7EZ2c?MK?rDvt>&OSj<;T7 z`6ipT!>e7Df^s7_6syGCx=b0ob?HPq0gb#UCRvh}-_9(RtSTLs){;p~VZnmR8^H|KNk)@gea zmd>j8?@EaGO)>Jg-$Y8h2gmCvk=nsA+XeT}weio)_B9=&T`FpE-CC|&_PmnSnQS(Z za;bGgpB1Zp0TgYup<)MHubOM67xnD9A8=mb$F18LFPXL>zf=*9A)Cm zTg%pL_OMg=3XfD$3IiD&fMUpI0IxW^)W4-tnl13Tqh=9gx}6-@o<3_FhR7e*&@RdC z5;vh7me;c%J9FH_6rWwP{VvGvoa2XZvq{#(V2$m1`RSjw_9x|#Y@iMgRekLZptHRA zOnZgxWdi`8C>F*LlZ)5l!J83{`u1;Z^6R$@^O?H;@^0Xe%?45YS}{R1fE|cJ zT$9MP{T1n_YS&(vhGMT8H;?zVlOHlFgbK^fv{Kr;utv-;>AOyMcxZr$Bf3ManjQ26 z`*`4?KKkgJm+5c6O1gD~dm#~4d&Wc_wT-IY=#IGtCF_xmhdGC<3C3-Y4c3P5Oqfj< zbPpPj#GH>m`A8=Yy=@v?4WDkEEJ0|;1zdc4At|Akt=S0opZda+*ko^h9iWnJOb|WI zcsbmcaqx4FHA~XnZJ3}E@kK9!vfcVlG2+QUgO1TI;;mCzKJ>1n-UE9RY@zN`%06jN z#>!~#l`+Uo`gAqeP@jv7Xx%n+2QswLflM^m)|UoyIdZuR6-9z)f++}!MBLYdT zBpq86;!w?v2X5O&CuY&+0_k(wqos+-IAgJu>sQxEZlNrLBHd9p;_^e_W7vsvZSe4a5o5FT;WS?q8T z5c4~o`x1Yi7i?@B#(h{MRI_%WJAx~p#*vBYtpses>_N;Cz&17$b#EqG)@-$$2N9t1 zSn86x5k_~Gdr;KWvhpNmTkMB%Izz`(BUG1lvjiHEUox1_Dtr_-OJCU+K6-91%9ktl zS`b=CDAmH7T-JmN#H&A+wL>OmT5W}S^_Sd+$|>zD-JqiD63*9K&*aD2_D`D; zv-{il!m0e&){s`^SBjS=edv$%-@JIpj}Ttiw#Yr+Pm3*3j7Ui9^i<_0S29d@5k!X( zsMG}61yFkNvO-&g`t2~pS;Q*Uyew0*5e;eKG1zN0{FlK@wXtgUt!I$2YJ1eFfApT8 z%yHOF?eJQKh&ssaa+^Fs5tNJTvbd&b2F-lP%`0=)za8YuaA_UIfyJOAeC$l8CKo8U zZ!WVBD~IAbXKi6aA{WzRNu}ztTS|;2j>T%3DA)kna!mOrv(C_bLd^(YsQu3jbAOFN=K<$(@ZINcQy( zw(_b~(Fc&)P8exHdGWMISUdFpcY>jlCO1pN`SH!nGlxYua#E@J}sq z;g3-{^G=})jtMHY)D`D_nSaB_f@&{1|G3R8$d`=P!^ZD2$R>+^8|?ydQ1A0-THQXK zwwJzrzblHW0?p#ZRdBI5yZrMcfmu3@PaSsN@tb?~=M0TXqH3;4Xg}X$)v7*(Stiq( zN8ag#)&_D4nA$Jl{dQjr*zP4oTO4-LFo3PA*o~|e-<3C`1A=hO(zV@`3s+O1Ka_6jQyISi4HDk#|J~8OGFWvoZh7wgSX4rxU*K-!xYbILG z%=g-$rO(pf@paY-o^Usyuww;4x3_e?p1Q+Km>)KT4N(F(GrXVM%`n*lZ}fTV-|_5M zSiw(9LIJ&jD2kZzaLb_>HR$AQNL$@I_ocaZI#6!TXE@6K$4-BH$<2TH8g?-pk=DJZ zZAi8|xg=ldH|YSJVS=Zn?^sD0Up*hC(`FI}t-?@+Q>XGYixtfGyE$nVOY{5VS_VUd zcz37y5rWYE5vaq{BOIjxS?kB3b$9kS2ga04v=K6b9S4>DGHT^4)|Nh5uhmv}TEUDk$9oT#8W~z+PU8<2wvB@|0_blxxHIdvymS-JnIK zDPrG-KHTBdzJWmzhN0fv>WSvqgy4J1MkF1-vT<%Qjk`(7_l$`(t!#}2im`HFWVfv8 zq2|~mG5% - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/front_end/Tests.js b/front_end/Tests.js index 81e0557550..af9d033d9a 100644 --- a/front_end/Tests.js +++ b/front_end/Tests.js @@ -978,7 +978,7 @@ TestSuite.prototype.evaluateInConsole_ = function(code, callback) function innerEvaluate() { WebInspector.context.removeFlavorChangeListener(WebInspector.ExecutionContext, showConsoleAndEvaluate, this); - var consoleView = WebInspector.ConsolePanel._view(); + var consoleView = WebInspector.ConsoleView.instance(); consoleView._prompt.setText(code); consoleView._promptElement.dispatchEvent(TestSuite.createKeyEvent("Enter")); diff --git a/front_end/animation/AnimationModel.js b/front_end/animation/AnimationModel.js index cbcb11098b..9831cb8873 100644 --- a/front_end/animation/AnimationModel.js +++ b/front_end/animation/AnimationModel.js @@ -771,7 +771,12 @@ WebInspector.AnimationModel.AnimationGroup.prototype = { return !error ? currentTime : 0; } - return this.target().animationAgent().getCurrentTime(this._animations[0].id(), callback).catchException(0); + var longestAnim = null; + for (var anim of this._animations) { + if (!longestAnim || anim.endTime() > longestAnim.endTime()) + longestAnim = anim; + } + return this.target().animationAgent().getCurrentTime(longestAnim.id(), callback).catchException(0); }, /** diff --git a/front_end/bindings/BlackboxManager.js b/front_end/bindings/BlackboxManager.js index 9e16e76f24..16e11e30c9 100644 --- a/front_end/bindings/BlackboxManager.js +++ b/front_end/bindings/BlackboxManager.js @@ -117,6 +117,7 @@ WebInspector.BlackboxManager.prototype = { if (!mappings.length) { if (previousScriptState.length > 0) return this._setScriptState(script, []); + return Promise.resolve(); } var currentBlackboxed = false; diff --git a/front_end/bindings/BreakpointManager.js b/front_end/bindings/BreakpointManager.js index 53446c5b79..867152495d 100644 --- a/front_end/bindings/BreakpointManager.js +++ b/front_end/bindings/BreakpointManager.js @@ -739,8 +739,7 @@ WebInspector.BreakpointManager.TargetBreakpoint = function(debuggerModel, breakp this._networkMapping = networkMapping; this._debuggerWorkspaceBinding = debuggerWorkspaceBinding; - /** @type {!Array.} */ - this._liveLocations = []; + this._liveLocations = new WebInspector.LiveLocationPool(); /** @type {!Object.} */ this._uiLocations = {}; @@ -763,10 +762,7 @@ WebInspector.BreakpointManager.TargetBreakpoint.prototype = { this._breakpoint._removeUILocation(uiLocations[i]); this._uiLocations = {}; - - for (var i = 0; i < this._liveLocations.length; ++i) - this._liveLocations[i].dispose(); - this._liveLocations = []; + this._liveLocations.disposeAll(); }, _scheduleUpdateInDebugger: function() @@ -944,7 +940,7 @@ WebInspector.BreakpointManager.TargetBreakpoint.prototype = { this._breakpoint.remove(); return false; } - this._liveLocations.push(this._debuggerWorkspaceBinding.createLiveLocation(location, this._locationUpdated.bind(this, location))); + this._debuggerWorkspaceBinding.createLiveLocation(location, this._locationUpdated.bind(this, location), this._liveLocations); return true; }, diff --git a/front_end/bindings/CSSWorkspaceBinding.js b/front_end/bindings/CSSWorkspaceBinding.js index 044b691ba2..41bb76a811 100644 --- a/front_end/bindings/CSSWorkspaceBinding.js +++ b/front_end/bindings/CSSWorkspaceBinding.js @@ -101,12 +101,13 @@ WebInspector.CSSWorkspaceBinding.prototype = { /** * @param {!WebInspector.CSSLocation} rawLocation * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool * @return {!WebInspector.CSSWorkspaceBinding.LiveLocation} */ - createLiveLocation: function(rawLocation, updateDelegate) + createLiveLocation: function(rawLocation, updateDelegate, locationPool) { var header = rawLocation.styleSheetId ? rawLocation.cssModel().styleSheetHeaderForId(rawLocation.styleSheetId) : null; - return new WebInspector.CSSWorkspaceBinding.LiveLocation(rawLocation.cssModel(), header, rawLocation, this, updateDelegate); + return new WebInspector.CSSWorkspaceBinding.LiveLocation(rawLocation.cssModel(), header, rawLocation, this, updateDelegate, locationPool); }, /** @@ -313,16 +314,17 @@ WebInspector.CSSWorkspaceBinding.HeaderInfo.prototype = { /** * @constructor - * @extends {WebInspector.LiveLocation} + * @extends {WebInspector.LiveLocationWithPool} * @param {!WebInspector.CSSModel} cssModel * @param {?WebInspector.CSSStyleSheetHeader} header * @param {!WebInspector.CSSLocation} rawLocation * @param {!WebInspector.CSSWorkspaceBinding} binding * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool */ -WebInspector.CSSWorkspaceBinding.LiveLocation = function(cssModel, header, rawLocation, binding, updateDelegate) +WebInspector.CSSWorkspaceBinding.LiveLocation = function(cssModel, header, rawLocation, binding, updateDelegate, locationPool) { - WebInspector.LiveLocation.call(this, updateDelegate); + WebInspector.LiveLocationWithPool.call(this, updateDelegate, locationPool); this._cssModel = cssModel; this._rawLocation = rawLocation; this._binding = binding; @@ -392,9 +394,12 @@ WebInspector.CSSWorkspaceBinding.LiveLocation.prototype = { return uiSourceCode.uiLocation(cssLocation.lineNumber, cssLocation.columnNumber); }, + /** + * @override + */ dispose: function() { - WebInspector.LiveLocation.prototype.dispose.call(this); + WebInspector.LiveLocationWithPool.prototype.dispose.call(this); if (this._header) this._binding._removeLiveLocation(this); this._cssModel.removeEventListener(WebInspector.CSSModel.Events.StyleSheetAdded, this._styleSheetAdded, this); @@ -410,7 +415,7 @@ WebInspector.CSSWorkspaceBinding.LiveLocation.prototype = { return false; }, - __proto__: WebInspector.LiveLocation.prototype + __proto__: WebInspector.LiveLocationWithPool.prototype } /** diff --git a/front_end/bindings/DebuggerWorkspaceBinding.js b/front_end/bindings/DebuggerWorkspaceBinding.js index 313b8a4d5f..ab89ed06a2 100644 --- a/front_end/bindings/DebuggerWorkspaceBinding.js +++ b/front_end/bindings/DebuggerWorkspaceBinding.js @@ -122,13 +122,14 @@ WebInspector.DebuggerWorkspaceBinding.prototype = { /** * @param {!WebInspector.DebuggerModel.Location} rawLocation * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool * @return {!WebInspector.DebuggerWorkspaceBinding.Location} */ - createLiveLocation: function(rawLocation, updateDelegate) + createLiveLocation: function(rawLocation, updateDelegate, locationPool) { var info = this._infoForScript(rawLocation.target(), rawLocation.scriptId); console.assert(info); - var location = new WebInspector.DebuggerWorkspaceBinding.Location(info._script, rawLocation, this, updateDelegate); + var location = new WebInspector.DebuggerWorkspaceBinding.Location(info._script, rawLocation, this, updateDelegate, locationPool); info._addLocation(location); return location; }, @@ -136,12 +137,13 @@ WebInspector.DebuggerWorkspaceBinding.prototype = { /** * @param {!Array} rawLocations * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool * @return {!WebInspector.LiveLocation} */ - createStackTraceTopFrameLiveLocation: function(rawLocations, updateDelegate) + createStackTraceTopFrameLiveLocation: function(rawLocations, updateDelegate, locationPool) { console.assert(rawLocations.length); - var location = new WebInspector.DebuggerWorkspaceBinding.StackTraceTopFrameLocation(rawLocations, this, updateDelegate); + var location = new WebInspector.DebuggerWorkspaceBinding.StackTraceTopFrameLocation(rawLocations, this, updateDelegate, locationPool); location.update(); return location; }, @@ -149,13 +151,14 @@ WebInspector.DebuggerWorkspaceBinding.prototype = { /** * @param {!WebInspector.DebuggerModel.Location} location * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool * @return {!WebInspector.DebuggerWorkspaceBinding.Location} */ - createCallFrameLiveLocation: function(location, updateDelegate) + createCallFrameLiveLocation: function(location, updateDelegate, locationPool) { var target = location.target(); this._ensureInfoForScript(location.script()); - var liveLocation = this.createLiveLocation(location, updateDelegate); + var liveLocation = this.createLiveLocation(location, updateDelegate, locationPool); this._registerCallFrameLiveLocation(target, liveLocation); return liveLocation; }, @@ -281,7 +284,7 @@ WebInspector.DebuggerWorkspaceBinding.prototype = { _reset: function(target) { var targetData = this._targetToData.get(target); - targetData.callFrameLocations.valuesArray().forEach(function(location) { location.dispose(); }); + targetData.callFrameLocations.valuesArray().forEach((location) => this._removeLiveLocation(location)); targetData.callFrameLocations.clear(); }, @@ -468,7 +471,7 @@ WebInspector.DebuggerWorkspaceBinding.ScriptInfo = function(script) /** @type {!Array.} */ this._sourceMappings = []; - /** @type {!Set.} */ + /** @type {!Set} */ this._locations = new Set(); } @@ -532,15 +535,16 @@ WebInspector.DebuggerWorkspaceBinding.ScriptInfo.prototype = { /** * @constructor - * @extends {WebInspector.LiveLocation} + * @extends {WebInspector.LiveLocationWithPool} * @param {!WebInspector.Script} script * @param {!WebInspector.DebuggerModel.Location} rawLocation * @param {!WebInspector.DebuggerWorkspaceBinding} binding * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool */ -WebInspector.DebuggerWorkspaceBinding.Location = function(script, rawLocation, binding, updateDelegate) +WebInspector.DebuggerWorkspaceBinding.Location = function(script, rawLocation, binding, updateDelegate, locationPool) { - WebInspector.LiveLocation.call(this, updateDelegate); + WebInspector.LiveLocationWithPool.call(this, updateDelegate, locationPool); this._script = script; this._rawLocation = rawLocation; this._binding = binding; @@ -562,7 +566,7 @@ WebInspector.DebuggerWorkspaceBinding.Location.prototype = { */ dispose: function() { - WebInspector.LiveLocation.prototype.dispose.call(this); + WebInspector.LiveLocationWithPool.prototype.dispose.call(this); this._binding._removeLiveLocation(this); }, @@ -575,25 +579,26 @@ WebInspector.DebuggerWorkspaceBinding.Location.prototype = { return WebInspector.blackboxManager.isBlackboxedRawLocation(this._rawLocation); }, - __proto__: WebInspector.LiveLocation.prototype + __proto__: WebInspector.LiveLocationWithPool.prototype } /** * @constructor - * @extends {WebInspector.LiveLocation} + * @extends {WebInspector.LiveLocationWithPool} * @param {!Array} rawLocations * @param {!WebInspector.DebuggerWorkspaceBinding} binding * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool */ -WebInspector.DebuggerWorkspaceBinding.StackTraceTopFrameLocation = function(rawLocations, binding, updateDelegate) +WebInspector.DebuggerWorkspaceBinding.StackTraceTopFrameLocation = function(rawLocations, binding, updateDelegate, locationPool) { - WebInspector.LiveLocation.call(this, updateDelegate); + WebInspector.LiveLocationWithPool.call(this, updateDelegate, locationPool); this._updateScheduled = true; - /** @type {!Array} */ - this._locations = []; + /** @type {!Set} */ + this._locations = new Set(); for (var location of rawLocations) - this._locations.push(binding.createLiveLocation(location, this._scheduleUpdate.bind(this))); + this._locations.add(binding.createCallFrameLiveLocation(location, this._scheduleUpdate.bind(this), locationPool)); this._updateLocation(); } @@ -604,25 +609,26 @@ WebInspector.DebuggerWorkspaceBinding.StackTraceTopFrameLocation.prototype = { */ uiLocation: function() { - return this._currentLocation().uiLocation(); + return this._current.uiLocation(); }, /** * @override + * @return {boolean} */ - dispose: function() + isBlackboxed: function() { - for (var location of this._locations) - location.dispose(); + return this._current.isBlackboxed(); }, /** * @override - * @return {boolean} */ - isBlackboxed: function() + dispose: function() { - return this._currentLocation().isBlackboxed(); + WebInspector.LiveLocationWithPool.prototype.dispose.call(this); + for (var location of this._locations) + location.dispose(); }, _scheduleUpdate: function() @@ -633,24 +639,20 @@ WebInspector.DebuggerWorkspaceBinding.StackTraceTopFrameLocation.prototype = { } }, - /** - * @return {!WebInspector.DebuggerWorkspaceBinding.Location} - */ - _currentLocation: function() - { - return this._locations[this._current < this._locations.length ? this._current : 0]; - }, - _updateLocation: function() { this._updateScheduled = false; - this._current = 0; - while (this._current < this._locations.length && this._locations[this._current].isBlackboxed()) - ++this._current; + this._current = this._locations.values().next().value; + for (var current of this._locations) { + if (!current.isBlackboxed()) { + this._current = current; + break; + } + } this.update(); }, - __proto__: WebInspector.LiveLocation.prototype + __proto__: WebInspector.LiveLocationWithPool.prototype } /** diff --git a/front_end/bindings/LiveLocation.js b/front_end/bindings/LiveLocation.js index 50bd67c120..12625e1b31 100644 --- a/front_end/bindings/LiveLocation.js +++ b/front_end/bindings/LiveLocation.js @@ -2,22 +2,49 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** @interface */ +WebInspector.LiveLocation = function() {} + +WebInspector.LiveLocation.prototype = { + update: function() {}, + + /** + * @return {?WebInspector.UILocation} + */ + uiLocation: function() {}, + + dispose: function() {}, + + /** + * @return {boolean} + */ + isBlackboxed: function() {} +} + /** * @constructor + * @implements {WebInspector.LiveLocation} * @param {function(!WebInspector.LiveLocation)} updateDelegate + * @param {!WebInspector.LiveLocationPool} locationPool */ -WebInspector.LiveLocation = function(updateDelegate) +WebInspector.LiveLocationWithPool = function(updateDelegate, locationPool) { this._updateDelegate = updateDelegate; + this._locationPool = locationPool; + this._locationPool._add(this); } -WebInspector.LiveLocation.prototype = { +WebInspector.LiveLocationWithPool.prototype = { + /** + * @override + */ update: function() { this._updateDelegate(this); }, /** + * @override * @return {?WebInspector.UILocation} */ uiLocation: function() @@ -25,12 +52,17 @@ WebInspector.LiveLocation.prototype = { throw "Not implemented"; }, + /** + * @override + */ dispose: function() { - // Overridden by subclasses. + this._locationPool._delete(this); + this._updateDelegate = null; }, /** + * @override * @return {boolean} */ isBlackboxed: function() @@ -38,3 +70,35 @@ WebInspector.LiveLocation.prototype = { throw "Not implemented"; } } + +/** + * @constructor + */ +WebInspector.LiveLocationPool = function() +{ + this._locations = new Set(); +} + +WebInspector.LiveLocationPool.prototype = { + /** + * @param {!WebInspector.LiveLocation} location + */ + _add: function(location) + { + this._locations.add(location); + }, + + /** + * @param {!WebInspector.LiveLocation} location + */ + _delete: function(location) + { + this._locations.delete(location); + }, + + disposeAll: function() + { + for (var location of this._locations) + location.dispose(); + } +} diff --git a/front_end/bindings/PresentationConsoleMessageHelper.js b/front_end/bindings/PresentationConsoleMessageHelper.js index 7d54c7041d..77f57d9566 100644 --- a/front_end/bindings/PresentationConsoleMessageHelper.js +++ b/front_end/bindings/PresentationConsoleMessageHelper.js @@ -48,6 +48,8 @@ WebInspector.PresentationConsoleMessageHelper = function(workspace) WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.ParsedScriptSource, this._parsedScriptSource, this); WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.FailedToParseScriptSource, this._parsedScriptSource, this); WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.GlobalObjectCleared, this._debuggerReset, this); + + this._locationPool = new WebInspector.LiveLocationPool(); } WebInspector.PresentationConsoleMessageHelper.prototype = { @@ -101,7 +103,7 @@ WebInspector.PresentationConsoleMessageHelper.prototype = { */ _addConsoleMessageToScript: function(message, rawLocation) { - this._presentationConsoleMessages.push(new WebInspector.PresentationConsoleMessage(message, rawLocation)); + this._presentationConsoleMessages.push(new WebInspector.PresentationConsoleMessage(message, rawLocation, this._locationPool)); }, /** @@ -151,6 +153,7 @@ WebInspector.PresentationConsoleMessageHelper.prototype = { for (var i = 0; i < this._presentationConsoleMessages.length; ++i) this._presentationConsoleMessages[i].dispose(); this._presentationConsoleMessages = []; + this._locationPool.disposeAll(); }, _debuggerReset: function() @@ -163,12 +166,13 @@ WebInspector.PresentationConsoleMessageHelper.prototype = { * @constructor * @param {!WebInspector.ConsoleMessage} message * @param {!WebInspector.DebuggerModel.Location} rawLocation + * @param {!WebInspector.LiveLocationPool} locationPool */ -WebInspector.PresentationConsoleMessage = function(message, rawLocation) +WebInspector.PresentationConsoleMessage = function(message, rawLocation, locationPool) { this._text = message.messageText; this._level = message.level === WebInspector.ConsoleMessage.MessageLevel.Error ? WebInspector.UISourceCode.Message.Level.Error : WebInspector.UISourceCode.Message.Level.Warning; - this._liveLocation = WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this._updateLocation.bind(this)); + WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this._updateLocation.bind(this), locationPool); } WebInspector.PresentationConsoleMessage.prototype = { @@ -187,7 +191,6 @@ WebInspector.PresentationConsoleMessage.prototype = { dispose: function() { - this._liveLocation.dispose(); if (this._uiMessage) this._uiMessage.remove(); } diff --git a/front_end/bindings/SASSSourceMapping.js b/front_end/bindings/SASSSourceMapping.js index 6041cebdea..4bb7c5f137 100644 --- a/front_end/bindings/SASSSourceMapping.js +++ b/front_end/bindings/SASSSourceMapping.js @@ -42,6 +42,7 @@ WebInspector.SASSSourceMapping = function(cssModel, networkMapping, networkProje this._networkMapping = networkMapping; this._cssModel.addEventListener(WebInspector.CSSModel.Events.SourceMapAttached, this._sourceMapAttached, this); this._cssModel.addEventListener(WebInspector.CSSModel.Events.SourceMapDetached, this._sourceMapDetached, this); + this._cssModel.addEventListener(WebInspector.CSSModel.Events.SourceMapChanged, this._sourceMapChanged, this); } WebInspector.SASSSourceMapping.prototype = { @@ -70,6 +71,32 @@ WebInspector.SASSSourceMapping.prototype = { WebInspector.cssWorkspaceBinding.updateLocations(header); }, + /** + * @param {!WebInspector.Event} event + */ + _sourceMapChanged: function(event) + { + var sourceMap = /** @type {!WebInspector.SourceMap} */(event.data.sourceMap); + var newSources = /** @type {!Map} */(event.data.newSources); + var headers = this._cssModel.headersForSourceMap(sourceMap); + var handledUISourceCodes = new Set(); + for (var header of headers) { + WebInspector.cssWorkspaceBinding.updateLocations(header); + for (var sourceURL of newSources.keys()) { + var uiSourceCode = this._networkMapping.uiSourceCodeForStyleURL(sourceURL, header); + if (!uiSourceCode) { + console.error("Failed to update source for " + sourceURL); + continue; + } + if (handledUISourceCodes.has(uiSourceCode)) + continue; + handledUISourceCodes.add(uiSourceCode); + var sassText = /** @type {string} */(newSources.get(sourceURL)); + uiSourceCode.addRevision(sassText); + } + } + }, + /** * @param {!WebInspector.CSSStyleSheetHeader} header */ diff --git a/front_end/common/ResourceType.js b/front_end/common/ResourceType.js index 4cdb4dd318..6e343109eb 100644 --- a/front_end/common/ResourceType.js +++ b/front_end/common/ResourceType.js @@ -190,7 +190,7 @@ WebInspector.resourceTypes = { Other: new WebInspector.ResourceType("other", "Other", WebInspector.resourceCategories.Other, false), SourceMapScript: new WebInspector.ResourceType("sm-script", "Script", WebInspector.resourceCategories.Script, false), SourceMapStyleSheet: new WebInspector.ResourceType("sm-stylesheet", "Stylesheet", WebInspector.resourceCategories.Stylesheet, false), - Manifest: new WebInspector.ResourceType("manifest", "Manifest", WebInspector.resourceCategories.Manifest, false), + Manifest: new WebInspector.ResourceType("manifest", "Manifest", WebInspector.resourceCategories.Manifest, true), } /** diff --git a/front_end/common/Text.js b/front_end/common/Text.js index 4e02e37d91..3e62f17a72 100644 --- a/front_end/common/Text.js +++ b/front_end/common/Text.js @@ -95,3 +95,52 @@ WebInspector.Text.prototype = { return this._value.substr(sourceRange.offset, sourceRange.length); }, } + +/** + * @constructor + * @param {!Array} lineEndings + */ +WebInspector.TextCursor = function(lineEndings) +{ + this._lineEndings = lineEndings; + this._offset = 0; + this._lineNumber = 0; + this._columnNumber = 0; +} + +WebInspector.TextCursor.prototype = { + /** + * @param {number} offset + */ + advance: function(offset) + { + this._offset = offset; + while (this._lineNumber < this._lineEndings.length && this._lineEndings[this._lineNumber] < this._offset) + ++this._lineNumber; + this._columnNumber = this._lineNumber ? this._offset - this._lineEndings[this._lineNumber - 1] - 1 : this._offset; + }, + + /** + * @return {number} + */ + offset: function() + { + return this._offset; + }, + + /** + * @return {number} + */ + lineNumber: function() + { + return this._lineNumber; + }, + + /** + * @return {number} + */ + columnNumber: function() + { + return this._columnNumber; + } +} diff --git a/front_end/common/TextRange.js b/front_end/common/TextRange.js index 661674001f..e478d29bc3 100644 --- a/front_end/common/TextRange.js +++ b/front_end/common/TextRange.js @@ -196,15 +196,6 @@ WebInspector.TextRange.prototype = { this.startColumn === other.startColumn && this.endColumn === other.endColumn; }, - /** - * @param {number} lineOffset - * @return {!WebInspector.TextRange} - */ - shift: function(lineOffset) - { - return new WebInspector.TextRange(this.startLine + lineOffset, this.startColumn, this.endLine + lineOffset, this.endColumn); - }, - /** * @param {number} line * @param {number} column @@ -355,3 +346,12 @@ WebInspector.SourceEdit.prototype = { }, } +/** + * @param {!WebInspector.SourceEdit} edit1 + * @param {!WebInspector.SourceEdit} edit2 + * @return {number} + */ +WebInspector.SourceEdit.comparator = function(edit1, edit2) +{ + return WebInspector.TextRange.comparator(edit1.oldRange, edit2.oldRange); +} diff --git a/front_end/components/Linkifier.js b/front_end/components/Linkifier.js index 6e70d88d6d..fe34672a41 100644 --- a/front_end/components/Linkifier.js +++ b/front_end/components/Linkifier.js @@ -52,8 +52,10 @@ WebInspector.LinkifierFormatter.prototype = { WebInspector.Linkifier = function(formatter) { this._formatter = formatter || new WebInspector.Linkifier.DefaultFormatter(WebInspector.Linkifier.MaxLengthForDisplayedURLs); - /** @type {!Map.>}*/ - this._liveLocationsByTarget = new Map(); + /** @type {!Map>} */ + this._anchorsByTarget = new Map(); + /** @type {!Map} */ + this._locationPoolByTarget = new Map(); WebInspector.targetManager.observeTargets(this); } @@ -115,6 +117,7 @@ WebInspector.Linkifier.linkifyUsingRevealer = function(revealable, text, fallbac WebInspector.Linkifier._uiLocationSymbol = Symbol("uiLocation"); WebInspector.Linkifier._fallbackAnchorSymbol = Symbol("fallbackAnchor"); +WebInspector.Linkifier._liveLocationSymbol = Symbol("liveLocation"); WebInspector.Linkifier.prototype = { /** @@ -123,7 +126,8 @@ WebInspector.Linkifier.prototype = { */ targetAdded: function(target) { - this._liveLocationsByTarget.set(target, new Map()); + this._anchorsByTarget.set(target, []); + this._locationPoolByTarget.set(target, new WebInspector.LiveLocationPool()); }, /** @@ -132,12 +136,11 @@ WebInspector.Linkifier.prototype = { */ targetRemoved: function(target) { - var liveLocations = this._liveLocationsByTarget.remove(target); - var anchors = liveLocations.keysArray(); - for (var i = 0; i < anchors.length; ++i) { - var anchor = anchors[i]; - var location = liveLocations.get(anchor); - delete anchor[WebInspector.Linkifier._uiLocationSymbol]; + var locationPool = /** @type {!WebInspector.LiveLocationPool} */(this._locationPoolByTarget.remove(target)); + locationPool.disposeAll(); + var anchors = this._anchorsByTarget.remove(target); + for (var anchor of anchors) { + delete anchor[WebInspector.Linkifier._liveLocationSymbol]; var fallbackAnchor = anchor[WebInspector.Linkifier._fallbackAnchorSymbol]; if (fallbackAnchor) { anchor.href = fallbackAnchor.href; @@ -147,7 +150,6 @@ WebInspector.Linkifier.prototype = { anchor.textContent = fallbackAnchor.textContent; delete anchor[WebInspector.Linkifier._fallbackAnchorSymbol]; } - location.dispose(); } }, @@ -175,8 +177,10 @@ WebInspector.Linkifier.prototype = { return fallbackAnchor; var anchor = this._createAnchor(classes); - var liveLocation = WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this._updateAnchor.bind(this, anchor)); - this._liveLocationsByTarget.get(rawLocation.target()).set(anchor, liveLocation); + var liveLocation = WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this._updateAnchor.bind(this, anchor), /** @type {!WebInspector.LiveLocationPool} */(this._locationPoolByTarget.get(rawLocation.target()))); + var anchors = /** @type {!Array} */(this._anchorsByTarget.get(rawLocation.target())); + anchors.push(anchor); + anchor[WebInspector.Linkifier._liveLocationSymbol] = liveLocation; anchor[WebInspector.Linkifier._fallbackAnchorSymbol] = fallbackAnchor; return anchor; }, @@ -224,8 +228,10 @@ WebInspector.Linkifier.prototype = { return fallbackAnchor; var anchor = this._createAnchor(classes); - var liveLocation = WebInspector.debuggerWorkspaceBinding.createStackTraceTopFrameLiveLocation(rawLocations, this._updateAnchor.bind(this, anchor)); - this._liveLocationsByTarget.get(target).set(anchor, liveLocation); + var liveLocation = WebInspector.debuggerWorkspaceBinding.createStackTraceTopFrameLiveLocation(rawLocations, this._updateAnchor.bind(this, anchor), /** @type {!WebInspector.LiveLocationPool} */(this._locationPoolByTarget.get(target))); + var anchors = /** @type {!Array} */(this._anchorsByTarget.get(target)); + anchors.push(anchor); + anchor[WebInspector.Linkifier._liveLocationSymbol] = liveLocation; anchor[WebInspector.Linkifier._fallbackAnchorSymbol] = fallbackAnchor; return anchor; }, @@ -238,8 +244,10 @@ WebInspector.Linkifier.prototype = { linkifyCSSLocation: function(rawLocation, classes) { var anchor = this._createAnchor(classes); - var liveLocation = WebInspector.cssWorkspaceBinding.createLiveLocation(rawLocation, this._updateAnchor.bind(this, anchor)); - this._liveLocationsByTarget.get(rawLocation.target()).set(anchor, liveLocation); + var liveLocation = WebInspector.cssWorkspaceBinding.createLiveLocation(rawLocation, this._updateAnchor.bind(this, anchor), /** @type {!WebInspector.LiveLocationPool} */(this._locationPoolByTarget.get(rawLocation.target()))); + var anchors = /** @type {!Array} */(this._anchorsByTarget.get(rawLocation.target())); + anchors.push(anchor); + anchor[WebInspector.Linkifier._liveLocationSymbol] = liveLocation; return anchor; }, @@ -251,12 +259,10 @@ WebInspector.Linkifier.prototype = { { delete anchor[WebInspector.Linkifier._uiLocationSymbol]; delete anchor[WebInspector.Linkifier._fallbackAnchorSymbol]; - var liveLocations = this._liveLocationsByTarget.get(target); - if (!liveLocations) - return; - var location = liveLocations.remove(anchor); - if (location) - location.dispose(); + var liveLocation = anchor[WebInspector.Linkifier._liveLocationSymbol]; + if (liveLocation) + liveLocation.dispose(); + delete anchor[WebInspector.Linkifier._liveLocationSymbol]; }, /** @@ -289,9 +295,7 @@ WebInspector.Linkifier.prototype = { reset: function() { - var targets = this._liveLocationsByTarget.keysArray(); - for (var i = 0; i < targets.length; ++i) { - var target = targets[i]; + for (var target of this._anchorsByTarget.keysArray()) { this.targetRemoved(target); this.targetAdded(target); } @@ -299,9 +303,9 @@ WebInspector.Linkifier.prototype = { dispose: function() { - this.reset(); + for (var target of this._anchorsByTarget.keysArray()) + this.targetRemoved(target); WebInspector.targetManager.unobserveTargets(this); - this._liveLocationsByTarget.clear(); }, /** diff --git a/front_end/components/NetworkConditionsSelector.js b/front_end/components/NetworkConditionsSelector.js index 4c2db2df48..bf851ab09e 100644 --- a/front_end/components/NetworkConditionsSelector.js +++ b/front_end/components/NetworkConditionsSelector.js @@ -39,7 +39,7 @@ WebInspector.NetworkConditionsSelector._throughputText = function(throughput) /** @type {!Array.} */ WebInspector.NetworkConditionsSelector._presets = [ - {title: "Offline", download: 0 * 1024 / 8, upload: 0 * 1024 / 8, latency: 0}, + WebInspector.NetworkManager.OfflineConditions, {title: "GPRS", download: 50 * 1024 / 8, upload: 20 * 1024 / 8, latency: 500}, {title: "Regular 2G", download: 250 * 1024 / 8, upload: 50 * 1024 / 8, latency: 300}, {title: "Good 2G", download: 450 * 1024 / 8, upload: 150 * 1024 / 8, latency: 150}, @@ -434,3 +434,32 @@ WebInspector.NetworkConditionsSettingsTab.prototype = { __proto__: WebInspector.VBox.prototype } + +/** + * @constructor + * @implements {WebInspector.ActionDelegate} + */ +WebInspector.NetworkConditionsActionDelegate = function() +{ +} + +WebInspector.NetworkConditionsActionDelegate.prototype = { + /** + * @override + * @param {!WebInspector.Context} context + * @param {string} actionId + * @return {boolean} + */ + handleAction: function(context, actionId) + { + if (actionId === "components.network-online") { + WebInspector.multitargetNetworkManager.setNetworkConditions(WebInspector.NetworkManager.NoThrottlingConditions); + return true; + } + if (actionId === "components.network-offline") { + WebInspector.multitargetNetworkManager.setNetworkConditions(WebInspector.NetworkManager.OfflineConditions); + return true; + } + return false; + } +} diff --git a/front_end/components/RequestAppBannerActionDelegate.js b/front_end/components/RequestAppBannerActionDelegate.js new file mode 100644 index 0000000000..21f09f6ecd --- /dev/null +++ b/front_end/components/RequestAppBannerActionDelegate.js @@ -0,0 +1,29 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @constructor + * @implements {WebInspector.ActionDelegate} + */ +WebInspector.RequestAppBannerActionDelegate = function() +{ +} + +WebInspector.RequestAppBannerActionDelegate.prototype = { + /** + * @override + * @param {!WebInspector.Context} context + * @param {string} actionId + * @return {boolean} + */ + handleAction: function(context, actionId) + { + var target = WebInspector.targetManager.mainTarget(); + if (target && target.isPage()) { + target.pageAgent().requestAppBanner(); + WebInspector.console.show(); + } + return true; + } +} diff --git a/front_end/components/module.json b/front_end/components/module.json index b1fb6ddf9c..bb36a96929 100644 --- a/front_end/components/module.json +++ b/front_end/components/module.json @@ -21,6 +21,22 @@ "settingType": "array", "defaultValue": [] }, + { + "type": "@WebInspector.ActionDelegate", + "actionId": "components.network-offline", + "category": "Network", + "title": "Go offline", + "className": "WebInspector.NetworkConditionsActionDelegate", + "tags": "device" + }, + { + "type": "@WebInspector.ActionDelegate", + "actionId": "components.network-online", + "category": "Network", + "title": "Go online", + "className": "WebInspector.NetworkConditionsActionDelegate", + "tags": "device" + }, { "type": "settings-view", "name": "network-conditions", @@ -30,6 +46,13 @@ "settings": [ "customNetworkConditions" ] + }, + { + "type": "@WebInspector.ActionDelegate", + "category": "Mobile", + "actionId": "components.request-app-banner", + "className": "WebInspector.RequestAppBannerActionDelegate", + "title": "Trigger add to homescreen" } ], "dependencies": [ @@ -52,6 +75,7 @@ "ObjectPopoverHelper.js", "ObjectPropertiesSection.js", "RemoteObjectPreviewFormatter.js", + "RequestAppBannerActionDelegate.js", "ShortcutsScreen.js", "EventListenersUtils.js", "EventListenersView.js", diff --git a/front_end/console/ConsolePanel.js b/front_end/console/ConsolePanel.js index fae8e4491c..47a5027db5 100644 --- a/front_end/console/ConsolePanel.js +++ b/front_end/console/ConsolePanel.js @@ -33,18 +33,7 @@ WebInspector.ConsolePanel = function() { WebInspector.Panel.call(this, "console"); - this._view = WebInspector.ConsolePanel._view(); -} - -/** - * @return {!WebInspector.ConsoleView} - */ -WebInspector.ConsolePanel._view = function() -{ - if (!WebInspector.ConsolePanel._consoleView) - WebInspector.ConsolePanel._consoleView = new WebInspector.ConsoleView(); - - return WebInspector.ConsolePanel._consoleView; + this._view = WebInspector.ConsoleView.instance(); } WebInspector.ConsolePanel.prototype = { @@ -86,7 +75,7 @@ WebInspector.ConsolePanel.prototype = { */ searchableView: function() { - return WebInspector.ConsolePanel._view().searchableView(); + return WebInspector.ConsoleView.instance().searchableView(); }, __proto__: WebInspector.Panel.prototype @@ -103,7 +92,7 @@ WebInspector.ConsolePanel.WrapperView = function() WebInspector.ConsolePanel.WrapperView._instance = this; - this._view = WebInspector.ConsolePanel._view(); + this._view = WebInspector.ConsoleView.instance(); } WebInspector.ConsolePanel.WrapperView.prototype = { @@ -158,7 +147,7 @@ WebInspector.ConsolePanel.ConsoleRevealer.prototype = { */ reveal: function(object) { - var consoleView = WebInspector.ConsolePanel._view(); + var consoleView = WebInspector.ConsoleView.instance(); if (consoleView.isShowing()) { consoleView.focus(); return Promise.resolve(); diff --git a/front_end/console/ConsoleView.js b/front_end/console/ConsoleView.js index da86d912bc..851347821a 100644 --- a/front_end/console/ConsoleView.js +++ b/front_end/console/ConsoleView.js @@ -164,6 +164,12 @@ WebInspector.ConsoleView.prototype = { return this._searchableView; }, + _clearHistory: function() + { + this._consoleHistorySetting.set([]); + this._prompt.setHistoryData([]); + }, + /** * @param {!WebInspector.Event} event */ @@ -578,6 +584,7 @@ WebInspector.ConsoleView.prototype = { contextMenu.appendSeparator(); contextMenu.appendAction("console.clear"); + contextMenu.appendAction("console.clear.history"); contextMenu.appendItem(WebInspector.UIString("Save as..."), this._saveConsole.bind(this)); var request = consoleMessage ? consoleMessage.request : null; @@ -1274,6 +1281,16 @@ WebInspector.ConsoleGroup.prototype = { }, } +/** + * @return {!WebInspector.ConsoleView} + */ +WebInspector.ConsoleView.instance = function() +{ + if (!WebInspector.ConsoleView._instance) + WebInspector.ConsoleView._instance = new WebInspector.ConsoleView(); + return WebInspector.ConsoleView._instance; +} + /** * @constructor * @implements {WebInspector.ActionDelegate} @@ -1298,6 +1315,9 @@ WebInspector.ConsoleView.ActionDelegate.prototype = { case "console.clear": WebInspector.ConsoleModel.clearConsole(); return true; + case "console.clear.history": + WebInspector.ConsoleView.instance()._clearHistory(); + return true; } return false; } diff --git a/front_end/console/module.json b/front_end/console/module.json index 181d9e0804..2cdde5847b 100644 --- a/front_end/console/module.json +++ b/front_end/console/module.json @@ -48,6 +48,13 @@ } ] }, + { + "type": "@WebInspector.ActionDelegate", + "category": "Console", + "actionId": "console.clear.history", + "title": "Clear console history", + "className": "WebInspector.ConsoleView.ActionDelegate" + }, { "type": "setting", "category": "Console", diff --git a/front_end/devtools.js b/front_end/devtools.js index 84325f7a37..8733c4a949 100644 --- a/front_end/devtools.js +++ b/front_end/devtools.js @@ -1001,6 +1001,11 @@ function installBackwardsCompatibility() var styleElement = window.document.createElement("style"); styleElement.type = "text/css"; styleElement.textContent = "html /deep/ * { min-width: 0; min-height: 0; }"; + + // Support for quirky border-image behavior ( WebInspector.actionRegistry.action("elements.edit-as-html").execute()); + }, + __proto__: TreeElement.prototype } diff --git a/front_end/elements/StylesSidebarPane.js b/front_end/elements/StylesSidebarPane.js index 299c2fa4cd..fa507a75a5 100644 --- a/front_end/elements/StylesSidebarPane.js +++ b/front_end/elements/StylesSidebarPane.js @@ -218,10 +218,11 @@ WebInspector.StylesSidebarPane.prototype = { if (!node) return; + var fullRefresh = Runtime.experiments.isEnabled("liveSASS"); for (var section of this.allSections()) { if (section.isBlank) continue; - section.update(section === editedSection); + section.update(fullRefresh || section === editedSection); } if (this._filterRegex) @@ -2391,7 +2392,10 @@ WebInspector.StylePropertyTreeElement.prototype = { if (!isEditingName && this._parentPane._mouseDownTreeElementIsName) moveDirection = "backward"; } - this.editingCommitted((context.isEditingName ? this.name : this.value) || event.target.textContent, context, moveDirection); + var text = event.target.textContent; + if (!context.isEditingName) + text = this.value || text; + this.editingCommitted(text, context, moveDirection); } this._originalPropertyText = this.property.propertyText; diff --git a/front_end/emulation/DeviceModeView.js b/front_end/emulation/DeviceModeView.js index d4c2a4969a..4980cc9c7a 100644 --- a/front_end/emulation/DeviceModeView.js +++ b/front_end/emulation/DeviceModeView.js @@ -16,10 +16,9 @@ WebInspector.DeviceModeView = function() this._model = new WebInspector.DeviceModeModel(this._updateUI.bind(this)); this._mediaInspector = new WebInspector.MediaQueryInspector(() => this._model.appliedDeviceSize().width, this._model.setWidth.bind(this._model)); - // TODO(dgozman): remove CountUpdated event. - this._showMediaInspectorSetting = WebInspector.settings.createSetting("showMediaQueryInspector", false); + this._showMediaInspectorSetting = WebInspector.settings.moduleSetting("showMediaQueryInspector"); this._showMediaInspectorSetting.addChangeListener(this._updateUI, this); - this._showRulersSetting = WebInspector.settings.createSetting("emulation.showRulers", false); + this._showRulersSetting = WebInspector.settings.moduleSetting("emulation.showRulers"); this._showRulersSetting.addChangeListener(this._updateUI, this); this._topRuler = new WebInspector.DeviceModeView.Ruler(true, this._model.setWidthAndScaleToFit.bind(this._model)); @@ -352,6 +351,52 @@ WebInspector.DeviceModeView.prototype = { this._model.emulate(WebInspector.DeviceModeModel.Type.None, null, null); }, + captureScreenshot: function() + { + var mainTarget = WebInspector.targetManager.mainTarget(); + if (!mainTarget) + return; + mainTarget.pageAgent().captureScreenshot(screenshotCaptured.bind(this)); + + /** + * @param {?Protocol.Error} error + * @param {string} content + * @this {WebInspector.DeviceModeView} + */ + function screenshotCaptured(error, content) + { + if (error) + return; + + // Create a canvas to splice the images together. + var canvas = createElement("canvas"); + var ctx = canvas.getContext("2d"); + var screenRect = this._model.screenRect(); + canvas.width = screenRect.width; + canvas.height = screenRect.height; + // Add any available screen images. + if (this._model.screenImage()) { + var screenImage = new Image(); + screenImage.crossOrigin = "Anonymous"; + screenImage.srcset = this._model.screenImage(); + ctx.drawImage(screenImage, 0, 0, screenRect.width, screenRect.height); + } + var pageImage = new Image(); + pageImage.src = "data:image/png;base64," + content; + var visiblePageRect = this._model.visiblePageRect(); + ctx.drawImage(pageImage, visiblePageRect.left, visiblePageRect.top, visiblePageRect.width, visiblePageRect.height); + var mainFrame = mainTarget.resourceTreeModel.mainFrame; + var fileName = mainFrame ? mainFrame.url.trimURL().removeURLFragment() : ""; + if (this._model.type() === WebInspector.DeviceModeModel.Type.Device) + fileName += WebInspector.UIString("(%s)", this._model.device().title); + // Trigger download. + var link = createElement("a"); + link.download = fileName + ".png"; + link.href = canvas.toDataURL("image/png"); + link.click(); + } + }, + __proto__: WebInspector.VBox.prototype } diff --git a/front_end/emulation/DeviceModeWrapper.js b/front_end/emulation/DeviceModeWrapper.js index 51515ccc47..f1cfe7f1c7 100644 --- a/front_end/emulation/DeviceModeWrapper.js +++ b/front_end/emulation/DeviceModeWrapper.js @@ -29,6 +29,17 @@ WebInspector.DeviceModeWrapper.prototype = { this._showDeviceModeSetting.set(!this._showDeviceModeSetting.get()); }, + /** + * @return {boolean} + */ + _captureScreenshot: function() + { + if (!this._deviceModeView) + return false; + this._deviceModeView.captureScreenshot(); + return true; + }, + /** * @param {boolean} force */ @@ -80,12 +91,8 @@ WebInspector.DeviceModeWrapper.ActionDelegate.prototype = { WebInspector.DeviceModeView._wrapperInstance._toggleDeviceMode(); return true; } - if (actionId === "emulation.request-app-banner") { - var target = WebInspector.targetManager.mainTarget(); - if (target && target.isPage()) - target.pageAgent().requestAppBanner(); - return true; - } + if (actionId === "emulation.capture-screenshot") + return WebInspector.DeviceModeView._wrapperInstance._captureScreenshot(); } return false; } diff --git a/front_end/emulation/module.json b/front_end/emulation/module.json index 988c5234c0..0375d4f9a4 100644 --- a/front_end/emulation/module.json +++ b/front_end/emulation/module.json @@ -25,6 +25,18 @@ } ] }, + { + "type": "@WebInspector.ActionDelegate", + "actionId": "emulation.capture-screenshot", + "className": "WebInspector.DeviceModeWrapper.ActionDelegate", + "title": "Capture screenshot" + }, + { + "type": "context-menu-item", + "location": "deviceModeMenu/tools", + "order": 12, + "actionId": "emulation.capture-screenshot" + }, { "type": "@WebInspector.ToolbarItem.Provider", "actionId": "emulation.toggle-device-mode", @@ -32,6 +44,30 @@ "location": "main-toolbar-left", "order": 1 }, + { + "type": "setting", + "category": "Mobile", + "settingName": "showMediaQueryInspector", + "settingType": "boolean", + "defaultValue": false, + "options": [ + {"value": true, "title": "Show media queries"}, + {"value": false, "title": "Hide media queries"} + ], + "tags": "device" + }, + { + "type": "setting", + "category": "Mobile", + "settingName": "emulation.showRulers", + "settingType": "boolean", + "defaultValue": false, + "options": [ + {"value": true, "title": "Show rulers"}, + {"value": false, "title": "Hide rulers"} + ], + "tags": "device" + }, { "type": "settings-view", "name": "devices", @@ -64,18 +100,11 @@ "className": "WebInspector.SensorsView", "tags": "geolocation, accelerometer, device orientation" }, - { - "type": "@WebInspector.ActionDelegate", - "actionId": "emulation.request-app-banner", - "className": "WebInspector.DeviceModeWrapper.ActionDelegate", - "title": "Request app banner\u2026" - }, { "type": "context-menu-item", "location": "deviceModeMenu/tools", - "experiment": "appBanner", "order": 10, - "actionId": "emulation.request-app-banner" + "actionId": "components.request-app-banner" } ], "dependencies": [ diff --git a/front_end/formatter_worker/CSSFormatter.js b/front_end/formatter_worker/CSSFormatter.js index 5743e0deb5..3476327b90 100644 --- a/front_end/formatter_worker/CSSFormatter.js +++ b/front_end/formatter_worker/CSSFormatter.js @@ -59,14 +59,11 @@ WebInspector.CSSFormatter.prototype = { * @param {string} token * @param {?string} type * @param {number} startPosition - * @param {number} endPosition */ - _tokenCallback: function(token, type, startPosition, endPosition) + _tokenCallback: function(token, type, startPosition) { startPosition += this._fromOffset; - endPosition += this._fromOffset; var startLine = this._lineEndings.lowerBound(startPosition); - var endLine = this._lineEndings.lowerBound(endPosition); if (startLine !== this._lastLine) this._state.eatWhitespace = true; if (/^property/.test(type) && !this._state.inPropertyValue) @@ -94,7 +91,7 @@ WebInspector.CSSFormatter.prototype = { this._state.afterClosingBrace = true; this._state.inPropertyValue = false; } else if (token === ":" && !this._state.inPropertyValue && this._state.seenProperty) { - this._builder.addToken(token, startPosition, startLine, endLine); + this._builder.addToken(token, startPosition); this._builder.addSoftSpace(); this._state.eatWhitespace = true; this._state.inPropertyValue = true; @@ -102,13 +99,13 @@ WebInspector.CSSFormatter.prototype = { return; } else if (token === "{") { this._builder.addSoftSpace(); - this._builder.addToken(token, startPosition, startLine, endLine); + this._builder.addToken(token, startPosition); this._builder.addNewLine(); this._builder.increaseNestingLevel(); return; } - this._builder.addToken(token, startPosition, startLine, endLine); + this._builder.addToken(token, startPosition); if (type === "comment" && !this._state.inPropertyValue && !this._state.seenProperty) this._builder.addNewLine(); diff --git a/front_end/formatter_worker/FormattedContentBuilder.js b/front_end/formatter_worker/FormattedContentBuilder.js index 4a5428be4f..3d1d266e87 100644 --- a/front_end/formatter_worker/FormattedContentBuilder.js +++ b/front_end/formatter_worker/FormattedContentBuilder.js @@ -17,7 +17,6 @@ WebInspector.FormattedContentBuilder = function(indentString) /** @type {!{original: !Array., formatted: !Array.}} */ this._mapping = { original: [0], formatted: [0] }; - this._lineNumber = 0; this._nestingLevel = 0; this._indentString = indentString; /** @type {!Map} */ @@ -31,15 +30,10 @@ WebInspector.FormattedContentBuilder = function(indentString) WebInspector.FormattedContentBuilder.prototype = { /** * @param {string} token - * @param {number} startPosition - * @param {number} startLine - * @param {number} endLine + * @param {number} offset */ - addToken: function(token, startPosition, startLine, endLine) + addToken: function(token, offset) { - if (this._lineNumber < startLine) - this._newLines = Math.max(this._newLines, startLine - this._lineNumber); - var last = this._formattedContent.peekLast(); if (last && /\w/.test(last[last.length - 1]) && /\w/.test(token)) this.addSoftSpace(); @@ -47,9 +41,8 @@ WebInspector.FormattedContentBuilder.prototype = { this._appendFormatting(); // Insert token. - this._addMappingIfNeeded(startPosition); + this._addMappingIfNeeded(offset); this._addText(token); - this._lineNumber = endLine; }, addSoftSpace: function() diff --git a/front_end/formatter_worker/FormatterWorker.js b/front_end/formatter_worker/FormatterWorker.js index 4d22f1b252..37014ddcfc 100644 --- a/front_end/formatter_worker/FormatterWorker.js +++ b/front_end/formatter_worker/FormatterWorker.js @@ -72,11 +72,112 @@ self.onmessage = function(event) { case "javaScriptOutline": WebInspector.javaScriptOutline(params.content); break; + case "javaScriptIdentifiers": + WebInspector.javaScriptIdentifiers(params.content); + break; + case "evaluatableJavaScriptSubstring": + WebInspector.evaluatableJavaScriptSubstring(params.content); + break; default: console.error("Unsupport method name: " + method); } }; +/** + * @param {string} content + */ +WebInspector.evaluatableJavaScriptSubstring = function(content) +{ + var tokenizer = acorn.tokenizer(content, {ecmaVersion: 6}); + var result = ""; + try { + var token = tokenizer.getToken(); + while (token.type !== acorn.tokTypes.eof && WebInspector.AcornTokenizer.punctuator(token)) + token = tokenizer.getToken(); + + var startIndex = token.start; + var endIndex = token.end; + var openBracketsCounter = 0; + while (token.type !== acorn.tokTypes.eof) { + var isIdentifier = WebInspector.AcornTokenizer.identifier(token); + var isThis = WebInspector.AcornTokenizer.keyword(token, "this"); + var isString = token.type === acorn.tokTypes.string; + if (!isThis && !isIdentifier && !isString) + break; + + endIndex = token.end; + token = tokenizer.getToken(); + while (WebInspector.AcornTokenizer.punctuator(token, ".[]")) { + if (WebInspector.AcornTokenizer.punctuator(token, "[")) + openBracketsCounter++; + + if (WebInspector.AcornTokenizer.punctuator(token, "]")) { + endIndex = openBracketsCounter > 0 ? token.end : endIndex; + openBracketsCounter--; + } + + token = tokenizer.getToken(); + } + } + result = content.substring(startIndex, endIndex); + } catch (e) { + console.error(e); + } + postMessage(result); +} + +/** + * @param {string} content + */ +WebInspector.javaScriptIdentifiers = function(content) +{ + var root = acorn.parse(content, {}); + /** @type {!Array} */ + var identifiers = []; + var functionDeclarationCounter = 0; + var walker = new WebInspector.ESTreeWalker(beforeVisit, afterVisit); + + /** + * @param {!ESTree.Node} node + * @return {boolean} + */ + function isFunction(node) + { + return node.type === "FunctionDeclaration" || node.type === "FunctionExpression"; + } + + /** + * @param {!ESTree.Node} node + */ + function beforeVisit(node) + { + if (isFunction(node)) + functionDeclarationCounter++; + + if (functionDeclarationCounter > 1) + return; + + if (isFunction(node) && node.params) + identifiers.pushAll(node.params); + + if (node.type === "VariableDeclarator") + identifiers.push(/** @type {!ESTree.Node} */(node.id)); + } + + /** + * @param {!ESTree.Node} node + */ + function afterVisit(node) + { + if (isFunction(node)) + functionDeclarationCounter--; + } + + walker.walk(root); + var reduced = identifiers.map(id => ({name: id.name, offset: id.start})); + postMessage(reduced); +} + /** * @param {string} mimeType * @param {string} text diff --git a/front_end/formatter_worker/HTMLFormatter.js b/front_end/formatter_worker/HTMLFormatter.js index ab7f46a5c2..9514125d63 100644 --- a/front_end/formatter_worker/HTMLFormatter.js +++ b/front_end/formatter_worker/HTMLFormatter.js @@ -49,9 +49,7 @@ WebInspector.HTMLFormatter.prototype = { tokenStart += fromOffset; tokenEnd += fromOffset; lastOffset = tokenEnd; - var startLine = lineEndings.lowerBound(tokenStart); - var endLine = lineEndings.lowerBound(tokenEnd); - this._builder.addToken(tokenValue, tokenStart, startLine, endLine); + this._builder.addToken(tokenValue, tokenStart); if (!type) return; diff --git a/front_end/formatter_worker/IdentityFormatter.js b/front_end/formatter_worker/IdentityFormatter.js index bafb5f2b1b..e428b7066a 100644 --- a/front_end/formatter_worker/IdentityFormatter.js +++ b/front_end/formatter_worker/IdentityFormatter.js @@ -21,9 +21,7 @@ WebInspector.IdentityFormatter.prototype = { format: function(text, lineEndings, fromOffset, toOffset) { var content = text.substring(fromOffset, toOffset); - var startLine = lineEndings.lowerBound(fromOffset); - var endLine = lineEndings.lowerBound(toOffset); - this._builder.addToken(content, fromOffset, startLine, endLine); + this._builder.addToken(content, fromOffset); } } diff --git a/front_end/formatter_worker/JavaScriptFormatter.js b/front_end/formatter_worker/JavaScriptFormatter.js index 7a8c611aa5..6a61ddce29 100644 --- a/front_end/formatter_worker/JavaScriptFormatter.js +++ b/front_end/formatter_worker/JavaScriptFormatter.js @@ -46,7 +46,6 @@ WebInspector.JavaScriptFormatter.prototype = { */ format: function(text, lineEndings, fromOffset, toOffset) { - this._lineOffset = lineEndings.lowerBound(fromOffset); this._fromOffset = fromOffset; this._toOffset = toOffset; this._content = text.substring(this._fromOffset, this._toOffset); @@ -74,7 +73,7 @@ WebInspector.JavaScriptFormatter.prototype = { else if (format[i] === "<") this._builder.decreaseNestingLevel(); else if (format[i] === "t") - this._builder.addToken(this._content.substring(token.start, token.end), this._fromOffset + token.start, this._lineOffset + this._tokenizer.tokenLineStart(), this._lineOffset + this._tokenizer.tokenLineEnd()); + this._builder.addToken(this._content.substring(token.start, token.end), this._fromOffset + token.start); } }, @@ -132,7 +131,7 @@ WebInspector.JavaScriptFormatter.prototype = { if (AT.lineComment(token)) return "tn"; if (AT.blockComment(token)) - return "t"; + return "tn"; if (node.type === "ContinueStatement" || node.type === "BreakStatement") { return node.label && AT.keyword(token) ? "ts" : "t"; } else if (node.type === "Identifier") { diff --git a/front_end/inspector.json b/front_end/inspector.json index 657336c6ce..f3a0a2b1dc 100644 --- a/front_end/inspector.json +++ b/front_end/inspector.json @@ -26,7 +26,6 @@ { "name": "cm_modes", "type": "remote" }, { "name": "settings" }, { "name": "layers" }, - { "name": "promises" }, { "name": "snippets" }, { "name": "diff" }, { "name": "sass" }, diff --git a/front_end/main/Main.js b/front_end/main/Main.js index b67f01d7f5..8302a039aa 100644 --- a/front_end/main/Main.js +++ b/front_end/main/Main.js @@ -103,7 +103,6 @@ WebInspector.Main.prototype = { { Runtime.experiments.register("accessibilityInspection", "Accessibility Inspection"); Runtime.experiments.register("applyCustomStylesheet", "Allow custom UI themes"); - Runtime.experiments.register("appBanner", "App banner support", true); Runtime.experiments.register("blackboxJSFramesOnTimeline", "Blackbox JavaScript frames on Timeline", true); Runtime.experiments.register("colorContrastRatio", "Contrast ratio line in color picker", true); Runtime.experiments.register("cpuThrottling", "CPU throttling", true); @@ -112,11 +111,11 @@ WebInspector.Main.prototype = { Runtime.experiments.register("layersPanel", "Layers panel"); Runtime.experiments.register("layoutEditor", "Layout editor", true); Runtime.experiments.register("inspectTooltip", "Dark inspect element tooltip"); + Runtime.experiments.register("liveSASS", "Live SASS", true); Runtime.experiments.register("multipleTimelineViews", "Multiple main views on Timeline", true); Runtime.experiments.register("networkRequestHeadersFilterInDetailsView", "Network request headers filter in details view", true); Runtime.experiments.register("networkRequestsOnTimeline", "Network requests on Timeline", true); Runtime.experiments.register("privateScriptInspection", "Private script inspection"); - Runtime.experiments.register("promiseTracker", "Promise inspector"); Runtime.experiments.register("reducedIndentation", "Reduced indentation in Elements DOM tree"); Runtime.experiments.register("requestBlocking", "Request blocking", true); Runtime.experiments.register("resolveVariableNames", "Resolve variable names", true); @@ -134,8 +133,6 @@ WebInspector.Main.prototype = { if (InspectorFrontendHost.isUnderTest()) { var testPath = JSON.parse(prefs["testPath"] || "\"\""); // Enable experiments for testing. - if (testPath.indexOf("debugger/promise") !== -1) - Runtime.experiments.enableForTest("promiseTracker"); if (testPath.indexOf("layers/") !== -1) Runtime.experiments.enableForTest("layersPanel"); if (testPath.indexOf("timeline/") !== -1 || testPath.indexOf("layers/") !== -1) diff --git a/front_end/main/module.json b/front_end/main/module.json index dfccf28766..0a441fdd47 100644 --- a/front_end/main/module.json +++ b/front_end/main/module.json @@ -59,10 +59,8 @@ }, { "type": "@WebInspector.ActionDelegate", - "category": "Navigation", "actionId": "main.debug-reload", "className": "WebInspector.Main.ReloadActionDelegate", - "title": "Clear cache & hard reload page", "bindings": [ { "shortcut": "Alt+R" diff --git a/front_end/network/NetworkLogView.js b/front_end/network/NetworkLogView.js index fe06f36582..4b7c2d7978 100644 --- a/front_end/network/NetworkLogView.js +++ b/front_end/network/NetworkLogView.js @@ -252,7 +252,7 @@ WebInspector.NetworkLogView.prototype = { _addFilters: function() { - this._textFilterUI = new WebInspector.TextFilterUI(); + this._textFilterUI = new WebInspector.TextFilterUI(true); this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._filterChanged, this); this._filterBar.addFilter(this._textFilterUI); @@ -966,11 +966,16 @@ WebInspector.NetworkLogView.prototype = { var node = this._nodesByRequestId.get(requestId); if (!node) continue; - if (!node[WebInspector.NetworkLogView._isFilteredOutSymbol]) - rootNode.removeChild(node); - node[WebInspector.NetworkLogView._isFilteredOutSymbol] = !this._applyFilter(node); - if (!node[WebInspector.NetworkLogView._isFilteredOutSymbol]) - nodesToInsert.push(node); + var isFilteredOut = !this._applyFilter(node); + if (node[WebInspector.NetworkLogView._isFilteredOutSymbol] !== isFilteredOut) { + if (!node[WebInspector.NetworkLogView._isFilteredOutSymbol]) + rootNode.removeChild(node); + + node[WebInspector.NetworkLogView._isFilteredOutSymbol] = isFilteredOut; + + if (!node[WebInspector.NetworkLogView._isFilteredOutSymbol]) + nodesToInsert.push(node); + } var request = node.request(); this._timeCalculator.updateBoundaries(request); this._durationCalculator.updateBoundaries(request); @@ -1570,7 +1575,7 @@ WebInspector.NetworkLogView.prototype = { */ supportsRegexSearch: function() { - return false; + return true; }, /** @@ -1634,8 +1639,14 @@ WebInspector.NetworkLogView.prototype = { */ _parseFilterQuery: function(query) { - var parsedQuery = this._suggestionBuilder.parseQuery(query); - this._filters = parsedQuery.text.map(this._createTextFilter); + var parsedQuery; + if (this._textFilterUI.isRegexChecked() && query !== "") + parsedQuery = {text: [query], filters: []}; + else + parsedQuery = this._suggestionBuilder.parseQuery(query); + + this._filters = parsedQuery.text.map(this._createTextFilter, this); + var n = parsedQuery.filters.length; for (var i = 0; i < n; ++i) { var filter = parsedQuery.filters[i]; @@ -1651,11 +1662,12 @@ WebInspector.NetworkLogView.prototype = { _createTextFilter: function(text) { var negative = false; - if (text[0] === "-" && text.length > 1) { + if (!this._textFilterUI.isRegexChecked() && text[0] === "-" && text.length > 1) { negative = true; text = text.substring(1); } - var regexp = new RegExp(text.escapeForRegExp(), "i"); + var regexp = this._textFilterUI.regex(); + var filter = WebInspector.NetworkLogView._requestNameOrPathFilter.bind(null, regexp); if (negative) filter = WebInspector.NetworkLogView._negativeFilter.bind(null, filter); @@ -1935,12 +1947,14 @@ WebInspector.NetworkLogView._negativeFilter = function(filter, request) } /** - * @param {!RegExp} regex + * @param {?RegExp} regex * @param {!WebInspector.NetworkRequest} request * @return {boolean} */ WebInspector.NetworkLogView._requestNameOrPathFilter = function(regex, request) { + if (!regex) + return false; return regex.test(request.name()) || regex.test(request.path()); } diff --git a/front_end/platform/utilities.js b/front_end/platform/utilities.js index a4a133262a..6025a3629d 100644 --- a/front_end/platform/utilities.js +++ b/front_end/platform/utilities.js @@ -1239,6 +1239,20 @@ Set.prototype.addAll = function(iterable) this.add(e); } +/** + * @param {!Iterable|!Array} iterable + * @return {boolean} + * @template T + */ +Set.prototype.containsAll = function(iterable) +{ + for (var e of iterable) { + if (!this.has(e)) + return false; + } + return true; +} + /** * @return {T} * @template T @@ -1312,6 +1326,19 @@ Multimap.prototype = { return this._map.has(key); }, + /** + * @param {K} key + * @param {V} value + * @return {boolean} + */ + hasValue: function(key, value) + { + var set = this._map.get(key); + if (!set) + return false; + return set.has(value); + }, + /** * @return {number} */ diff --git a/front_end/profiler/CPUProfileBottomUpDataGrid.js b/front_end/profiler/CPUProfileBottomUpDataGrid.js index 8f3f02db8f..591d4acbb6 100644 --- a/front_end/profiler/CPUProfileBottomUpDataGrid.js +++ b/front_end/profiler/CPUProfileBottomUpDataGrid.js @@ -172,9 +172,6 @@ WebInspector.BottomUpProfileDataGridNode._sharedPopulate = function(container) } } - for (var i = 0; i < container.children.length; ++i) - container.children[i].buildData(); - delete container._remainingNodeInfos; } @@ -325,10 +322,6 @@ WebInspector.BottomUpProfileDataGridTree.prototype = { return this._searchResults.length; }, - buildData: function() - { - }, - /** * @override */ diff --git a/front_end/profiler/CPUProfileDataGrid.js b/front_end/profiler/CPUProfileDataGrid.js index a8430b46a2..863127a0e2 100644 --- a/front_end/profiler/CPUProfileDataGrid.js +++ b/front_end/profiler/CPUProfileDataGrid.js @@ -52,100 +52,62 @@ WebInspector.ProfileDataGridNode = function(profileNode, owningTree, hasChildren WebInspector.ProfileDataGridNode.prototype = { /** * @override - * @param {string} columnIdentifier + * @param {string} columnId * @return {!Element} */ - createCell: function(columnIdentifier) + createCell: function(columnId) { - var cell = this._createValueCell(columnIdentifier) || WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier); - - if (columnIdentifier === "self" && this._searchMatchedSelfColumn) - cell.classList.add("highlight"); - else if (columnIdentifier === "total" && this._searchMatchedTotalColumn) - cell.classList.add("highlight"); - - if (columnIdentifier !== "function") + /** + * @param {number} value + * @param {number} percent + * @return {!Element} + */ + function createValueCell(value, percent) + { + var cell = createElementWithClass("td", "numeric-column"); + var div = cell.createChild("div", "profile-multiple-values"); + div.createChild("span").textContent = WebInspector.UIString("%.1f\u2009ms", value); + div.createChild("span", "percent-column").textContent = percent >= 0 ? WebInspector.UIString("%.2f\u2009%%", percent) : ""; return cell; + } - if (this._deoptReason) - cell.classList.add("not-optimized"); - - if (this._searchMatchedFunctionColumn) - cell.classList.add("highlight"); - - if (this.profileNode.scriptId !== "0") { + var cell; + var isIdleNode = this.profileNode === this.tree.profileView.profile.idleNode; + switch (columnId) { + case "self": + cell = createValueCell(this.selfTime, isIdleNode ? -1 : this.selfPercent); + cell.classList.toggle("highlight", this._searchMatchedSelfColumn); + break; + + case "total": + cell = createValueCell(this.totalTime, isIdleNode ? -1 : this.totalPercent); + cell.classList.toggle("highlight", this._searchMatchedTotalColumn); + break; + + case "function": + cell = this.createTD(columnId); + cell.classList.toggle("highlight", this._searchMatchedFunctionColumn); + if (this._deoptReason) { + cell.classList.add("not-optimized"); + cell.createChild("span", "profile-warn-marker").title = WebInspector.UIString("Not optimized: %s", this._deoptReason); + } + cell.createTextChild(this.functionName); + if (this.profileNode.scriptId === "0") + break; var target = this.tree.profileView.target(); var callFrame = /** @type {!RuntimeAgent.CallFrame} */ (this.profileNode); var urlElement = this.tree.profileView._linkifier.linkifyConsoleCallFrame(target, callFrame, "profile-node-file"); urlElement.style.maxWidth = "75%"; - cell.insertBefore(urlElement, cell.firstChild); - } - - return cell; - }, - - /** - * @param {string} columnIdentifier - * @return {?Element} - */ - _createValueCell: function(columnIdentifier) - { - if (columnIdentifier !== "self" && columnIdentifier !== "total") - return null; + cell.appendChild(urlElement); + break; - var cell = createElement("td"); - cell.className = "numeric-column"; - var div = createElement("div"); - var valueSpan = createElement("span"); - valueSpan.textContent = this.data[columnIdentifier]; - div.appendChild(valueSpan); - var percentColumn = columnIdentifier + "-percent"; - if (percentColumn in this.data) { - var percentSpan = createElement("span"); - percentSpan.className = "percent-column"; - percentSpan.textContent = this.data[percentColumn]; - div.appendChild(percentSpan); - div.classList.add("profile-multiple-values"); + default: + cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnId); + break; } - cell.appendChild(div); return cell; }, - buildData: function() - { - function formatMilliseconds(time) - { - return WebInspector.UIString("%.1f\u2009ms", time); - } - function formatPercent(value) - { - return WebInspector.UIString("%.2f\u2009%%", value); - } - - var functionName; - if (this._deoptReason) { - var content = createDocumentFragment(); - var marker = content.createChild("span", "profile-warn-marker"); - marker.title = WebInspector.UIString("Not optimized: %s", this._deoptReason); - content.createTextChild(this.functionName); - functionName = content; - } else { - functionName = this.functionName; - } - - this.data = { - "function": functionName, - "self-percent": formatPercent(this.selfPercent), - "self": formatMilliseconds(this.selfTime), - "total-percent": formatPercent(this.totalPercent), - "total": formatMilliseconds(this.totalTime), - }; - if (this.profileNode === this.tree.profileView.profile.idleNode) { - this.data['self-percent'] = undefined; - this.data['total-percent'] = undefined - } - }, - select: function(supressSelectedEvent) { WebInspector.DataGridNode.prototype.select.call(this, supressSelectedEvent); diff --git a/front_end/profiler/CPUProfileTopDownDataGrid.js b/front_end/profiler/CPUProfileTopDownDataGrid.js index 48445c8843..f28e58b314 100644 --- a/front_end/profiler/CPUProfileTopDownDataGrid.js +++ b/front_end/profiler/CPUProfileTopDownDataGrid.js @@ -36,7 +36,6 @@ WebInspector.TopDownProfileDataGridNode = function(profileNode, owningTree) WebInspector.ProfileDataGridNode.call(this, profileNode, owningTree, hasChildren); this._remainingChildren = profileNode.children; - this.buildData(); } WebInspector.TopDownProfileDataGridNode.prototype = { diff --git a/front_end/promises/PromisePane.js b/front_end/promises/PromisePane.js deleted file mode 100644 index 24381fe2a6..0000000000 --- a/front_end/promises/PromisePane.js +++ /dev/null @@ -1,791 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * @constructor - * @extends {WebInspector.VBox} - * @implements {WebInspector.TargetManager.Observer} - */ -WebInspector.PromisePane = function() -{ - WebInspector.VBox.call(this); - this.registerRequiredCSS("promises/promisePane.css"); - this.element.classList.add("promises"); - - var toolbar = new WebInspector.Toolbar("", this.element); - this._recordButton = new WebInspector.ToolbarToggle("", "record-toolbar-item"); - this._recordButton.addEventListener("click", this._recordButtonClicked.bind(this)); - toolbar.appendToolbarItem(this._recordButton); - - var clearButton = new WebInspector.ToolbarButton(WebInspector.UIString("Clear"), "clear-toolbar-item"); - clearButton.addEventListener("click", this._clearButtonClicked.bind(this)); - toolbar.appendToolbarItem(clearButton); - toolbar.appendSeparator(); - - this._promiseStatusFiltersSetting = WebInspector.settings.createSetting("promiseStatusFilters", {}); - this._hideCollectedPromisesSetting = WebInspector.settings.createSetting("hideCollectedPromises", false); - - this._createFilterBar(); - toolbar.appendToolbarItem(this._filterBar.filterButton()); - - var garbageCollectButton = new WebInspector.ToolbarButton(WebInspector.UIString("Collect garbage"), "garbage-collect-toolbar-item"); - garbageCollectButton.addEventListener("click", this._garbageCollectButtonClicked, this); - toolbar.appendToolbarItem(garbageCollectButton); - - toolbar.appendSeparator(); - var asyncCheckbox = new WebInspector.ToolbarCheckbox(WebInspector.UIString("Async"), WebInspector.UIString("Capture async stack traces"), WebInspector.moduleSetting("enableAsyncStackTraces")); - toolbar.appendToolbarItem(asyncCheckbox); - - this._filterBar.show(this.element); - - this._hiddenByFilterCount = 0; - this._filterStatusMessageElement = this.element.createChild("div", "promises-filter-status hidden"); - this._filterStatusTextElement = this._filterStatusMessageElement.createChild("span"); - this._filterStatusMessageElement.createTextChild(" "); - var resetFiltersLink = this._filterStatusMessageElement.createChild("span", "link"); - resetFiltersLink.textContent = WebInspector.UIString("Show all promises."); - resetFiltersLink.addEventListener("click", this._resetFilters.bind(this), true); - - // FIXME: Make "status" column width fixed to ~16px. - var columns = [ - { id: "status", weight: 1 }, - { id: "function", title: WebInspector.UIString("Function"), disclosure: true, weight: 10 }, - { id: "created", title: WebInspector.UIString("Created"), weight: 10 }, - { id: "settled", title: WebInspector.UIString("Settled"), weight: 10 }, - { id: "tts", title: WebInspector.UIString("Time to settle"), weight: 10 } - ]; - this._dataGrid = new WebInspector.ViewportDataGrid(columns, undefined, undefined, undefined, this._onContextMenu.bind(this)); - this._dataGrid.setStickToBottom(true); - this._dataGrid.asWidget().show(this.element); - - this._linkifier = new WebInspector.Linkifier(); - - /** @type {!Map.>} */ - this._promiseDetailsByDebuggerModel = new Map(); - /** @type {!Map.} */ - this._promiseIdToNode = new Map(); - - this._popoverHelper = new WebInspector.PopoverHelper(this.element, this._getPopoverAnchor.bind(this), this._showPopover.bind(this)); - this._popoverHelper.setTimeout(250, 250); - - this.element.addEventListener("click", this._hidePopover.bind(this), true); - - WebInspector.targetManager.addModelListener(WebInspector.DebuggerModel, WebInspector.DebuggerModel.Events.PromiseUpdated, this._onPromiseUpdated, this); - WebInspector.targetManager.addModelListener(WebInspector.ResourceTreeModel, WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, this._mainFrameNavigated, this); - WebInspector.context.addFlavorChangeListener(WebInspector.Target, this._targetChanged, this); - - WebInspector.targetManager.observeTargets(this); -} - -WebInspector.PromisePane._maxPromiseCount = 10000; - - -/** - * @constructor - * @param {!DebuggerAgent.PromiseDetails} details - */ -WebInspector.PromiseDetails = function(details) -{ - this.id = details.id; - this.isGarbageCollected = false; - this.update(details); -} - -WebInspector.PromiseDetails.prototype = { - /** - * @param {!DebuggerAgent.PromiseDetails} details - */ - update: function(details) - { - if (this.id !== details.id) - throw new Error("Invalid id, expected " + this.id + " was " + details.id); - if (details.status) - this.status = details.status; - if (details.parentId) - this.parentId = details.parentId; - if (details.creationTime) - this.creationTime = details.creationTime; - if (details.settlementTime) - this.settlementTime = details.settlementTime; - if (details.creationStack) { - this.creationStack = details.creationStack; - if (this.creationStack.callFrames.length) - this.callFrame = this.creationStack.callFrames[0]; - } - if (details.asyncCreationStack) - this.asyncCreationStack = details.asyncCreationStack; - if (details.settlementStack) - this.settlementStack = details.settlementStack; - if (details.asyncSettlementStack) - this.asyncSettlementStack = details.asyncSettlementStack; - } -} - - -WebInspector.PromisePane.prototype = { - _createFilterBar: function() - { - this._filterBar = new WebInspector.FilterBar("promisePane"); - - this._textFilterUI = new WebInspector.TextFilterUI(true); - this._textFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._onFilterChanged, this); - this._filterBar.addFilter(this._textFilterUI); - - var statuses = [ - { name: "pending", label: WebInspector.UIString("Pending") }, - { name: "resolved", label: WebInspector.UIString("Fulfilled") }, - { name: "rejected", label: WebInspector.UIString("Rejected") } - ]; - this._statusFilterUI = new WebInspector.NamedBitSetFilterUI(statuses, this._promiseStatusFiltersSetting); - this._statusFilterUI.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._onFilterChanged, this); - this._filterBar.addFilter(this._statusFilterUI); - - var hideCollectedCheckbox = new WebInspector.CheckboxFilterUI("hide-collected-promises", WebInspector.UIString("Hide collected promises"), true, this._hideCollectedPromisesSetting); - hideCollectedCheckbox.addEventListener(WebInspector.FilterUI.Events.FilterChanged, this._onFilterChanged, this); - this._filterBar.addFilter(hideCollectedCheckbox); - }, - - /** - * @param {!WebInspector.PromiseDetails} details - * @param {!WebInspector.DataGridNode} node - * @return {boolean} - */ - _shouldBeVisible: function(details, node) - { - if (!this._statusFilterUI.accept(details.status)) - return false; - - if (this._hideCollectedPromisesSetting.get() && details.isGarbageCollected) - return false; - - var regex = this._textFilterUI.regex(); - if (!regex) - return true; - - var text = node.dataTextForSearch(); - regex.lastIndex = 0; - return regex.test(text); - }, - - _onFilterChanged: function() - { - if (this._filterChangedTimeout) - clearTimeout(this._filterChangedTimeout); - this._filterChangedTimeout = setTimeout(onTimerFired.bind(this), 100); - - /** - * @this {WebInspector.PromisePane} - */ - function onTimerFired() - { - delete this._filterChangedTimeout; - this._refresh(); - } - }, - - /** - * @override - * @return {!Array.} - */ - elementsToRestoreScrollPositionsFor: function() - { - return [this._dataGrid.scrollContainer]; - }, - - /** - * @override - * @param {!WebInspector.Target} target - */ - targetAdded: function(target) - { - var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); - if (debuggerModel && this._enabled) - this._enablePromiseTracker(debuggerModel); - }, - - /** - * @override - * @param {!WebInspector.Target} target - */ - targetRemoved: function(target) - { - var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); - if (!debuggerModel) - return; - this._promiseDetailsByDebuggerModel.delete(debuggerModel); - if (this._debuggerModel === debuggerModel) { - this._clear(); - delete this._debuggerModel; - } - }, - - /** - * @param {!WebInspector.Event} event - */ - _targetChanged: function(event) - { - if (!this._enabled) - return; - var target = /** @type {!WebInspector.Target} */ (event.data); - var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); - if (!debuggerModel || this._debuggerModel === debuggerModel) - return; - this._debuggerModel = debuggerModel; - this._refresh(); - }, - - /** - * @param {!WebInspector.Event} event - */ - _mainFrameNavigated: function(event) - { - var frame = /** @type {!WebInspector.ResourceTreeFrame} */ (event.data); - var target = frame.target(); - var debuggerModel = WebInspector.DebuggerModel.fromTarget(target); - if (!debuggerModel) - return; - this._promiseDetailsByDebuggerModel.delete(debuggerModel); - if (this._debuggerModel === debuggerModel) - this._clear(); - }, - - /** @override */ - wasShown: function() - { - // Auto enable upon the very first show. - if (typeof this._enabled === "undefined") { - var target = WebInspector.context.flavor(WebInspector.Target); - this._debuggerModel = WebInspector.DebuggerModel.fromTarget(target); - this._updateRecordingState(true); - } - if (this._refreshIsNeeded) - this._refresh(); - }, - - /** - * @param {!WebInspector.DebuggerModel} debuggerModel - */ - _enablePromiseTracker: function(debuggerModel) - { - debuggerModel.enablePromiseTracker(true); - }, - - /** - * @param {!WebInspector.DebuggerModel} debuggerModel - */ - _disablePromiseTracker: function(debuggerModel) - { - debuggerModel.disablePromiseTracker(); - }, - - /** @override */ - willHide: function() - { - this._hidePopover(); - }, - - _hidePopover: function() - { - this._popoverHelper.hidePopover(); - }, - - _recordButtonClicked: function() - { - this._updateRecordingState(!this._recordButton.toggled()); - }, - - /** - * @param {boolean} enabled - */ - _updateRecordingState: function(enabled) - { - this._enabled = enabled; - this._recordButton.setToggled(this._enabled); - this._recordButton.setTitle(this._enabled ? WebInspector.UIString("Stop Recording Promises Log") : WebInspector.UIString("Record Promises Log")); - WebInspector.DebuggerModel.instances().forEach(this._enabled ? this._enablePromiseTracker : this._disablePromiseTracker, this); - }, - - _clearButtonClicked: function() - { - this._clear(); - if (this._debuggerModel) - this._promiseDetailsByDebuggerModel.delete(this._debuggerModel); - }, - - _resetFilters: function() - { - this._hideCollectedPromisesSetting.set(false); - this._promiseStatusFiltersSetting.set({}); - this._textFilterUI.setValue(""); - }, - - _updateFilterStatus: function() - { - this._filterStatusTextElement.textContent = WebInspector.UIString(this._hiddenByFilterCount === 1 ? "%d promise is hidden by filters." : "%d promises are hidden by filters.", this._hiddenByFilterCount); - this._filterStatusMessageElement.classList.toggle("hidden", !this._hiddenByFilterCount); - }, - - _garbageCollectButtonClicked: function() - { - var targets = WebInspector.targetManager.targets(); - for (var i = 0; i < targets.length; ++i) - targets[i].heapProfilerAgent().collectGarbage(); - }, - - /** - * @param {!WebInspector.DebuggerModel} debuggerModel - * @return {boolean} - */ - _truncateLogIfNeeded: function(debuggerModel) - { - var promiseIdToDetails = this._promiseDetailsByDebuggerModel.get(debuggerModel); - if (!promiseIdToDetails || promiseIdToDetails.size <= WebInspector.PromisePane._maxPromiseCount) - return false; - - var elementsToTruncate = WebInspector.PromisePane._maxPromiseCount / 10; - var sortedDetails = promiseIdToDetails.valuesArray().sort(compare); - for (var i = 0; i < elementsToTruncate; ++i) - promiseIdToDetails.delete(sortedDetails[i].id); - return true; - - /** - * @param {!WebInspector.PromiseDetails} x - * @param {!WebInspector.PromiseDetails} y - * @return {number} - */ - function compare(x, y) - { - var t1 = x.creationTime || 0; - var t2 = y.creationTime || 0; - return t1 - t2 || x.id - y.id; - } - }, - - /** - * @param {!WebInspector.Event} event - */ - _onPromiseUpdated: function(event) - { - var debuggerModel = /** @type {!WebInspector.DebuggerModel} */ (event.target); - var eventType = /** @type {string} */ (event.data.eventType); - var protocolDetails = /** @type {!DebuggerAgent.PromiseDetails} */ (event.data.promise); - - var promiseIdToDetails = this._promiseDetailsByDebuggerModel.get(debuggerModel); - if (!promiseIdToDetails) { - promiseIdToDetails = new Map(); - this._promiseDetailsByDebuggerModel.set(debuggerModel, promiseIdToDetails); - } - - var details = promiseIdToDetails.get(protocolDetails.id); - if (!details && eventType === "gc") - return; - - var truncated = this._truncateLogIfNeeded(debuggerModel); - if (details) - details.update(protocolDetails) - else - details = new WebInspector.PromiseDetails(protocolDetails); - promiseIdToDetails.set(details.id, details); - - if (eventType === "gc") - details.isGarbageCollected = true; - - if (debuggerModel === this._debuggerModel) { - if (!this.isShowing()) { - this._refreshIsNeeded = true; - return; - } - if (truncated || this._refreshIsNeeded) { - this._refresh(); - return; - } - - var node = /** @type {!WebInspector.DataGridNode} */ (this._promiseIdToNode.get(details.id)); - var wasVisible = !node || !node._isPromiseHidden; - - // Check for the fast path on GC events. - if (eventType === "gc" && node && node.parent && !this._hideCollectedPromisesSetting.get()) - node.update(details); - else - this._attachDataGridNode(details); - - var isVisible = this._shouldBeVisible(details, /** @type {!WebInspector.DataGridNode} */(this._promiseIdToNode.get(details.id))); - if (wasVisible !== isVisible) { - this._hiddenByFilterCount += wasVisible ? 1 : -1; - this._updateFilterStatus(); - } - } - }, - - /** - * @param {!WebInspector.PromiseDetails} details - */ - _attachDataGridNode: function(details) - { - var node = this._createDataGridNode(details); - var parentNode = this._findVisibleParentNodeDetails(details); - if (parentNode !== node.parent) - parentNode.appendChild(node); - if (this._shouldBeVisible(details, node)) - parentNode.expanded = true; - else - node.remove(); - }, - - /** - * @param {!WebInspector.PromiseDetails} details - * @return {!WebInspector.DataGridNode} - */ - _findVisibleParentNodeDetails: function(details) - { - var promiseIdToDetails = /** @type {!Map.} */ (this._promiseDetailsByDebuggerModel.get(this._debuggerModel)); - var currentDetails = details; - while (currentDetails) { - var parentId = currentDetails.parentId; - if (typeof parentId !== "number") - break; - currentDetails = promiseIdToDetails.get(parentId); - if (!currentDetails) - break; - var node = this._promiseIdToNode.get(currentDetails.id); - if (node && this._shouldBeVisible(currentDetails, node)) - return node; - } - return this._dataGrid.rootNode(); - }, - - /** - * @param {!WebInspector.PromiseDetails} details - * @return {!WebInspector.DataGridNode} - */ - _createDataGridNode: function(details) - { - var node = this._promiseIdToNode.get(details.id); - if (!node) { - node = new WebInspector.PromiseDataGridNode(details, this._debuggerModel, this._linkifier, this._dataGrid); - this._promiseIdToNode.set(details.id, node); - } else { - node.update(details); - } - return node; - }, - - _refresh: function() - { - delete this._refreshIsNeeded; - this._clear(); - if (!this._debuggerModel) - return; - if (!this._promiseDetailsByDebuggerModel.has(this._debuggerModel)) - return; - - var rootNode = this._dataGrid.rootNode(); - var promiseIdToDetails = /** @type {!Map.} */ (this._promiseDetailsByDebuggerModel.get(this._debuggerModel)); - - var nodesToInsert = { __proto__: null }; - // The for..of loop iterates in insertion order. - for (var pair of promiseIdToDetails) { - var id = /** @type {number} */ (pair[0]); - var details = /** @type {!WebInspector.PromiseDetails} */ (pair[1]); - var node = this._createDataGridNode(details); - node._isPromiseHidden = !this._shouldBeVisible(details, node); - if (node._isPromiseHidden) { - ++this._hiddenByFilterCount; - continue; - } - nodesToInsert[id] = { details: details, node: node }; - } - - for (var id in nodesToInsert) { - var node = nodesToInsert[id].node; - var details = nodesToInsert[id].details; - this._findVisibleParentNodeDetails(details).appendChild(node); - } - - for (var id in nodesToInsert) { - var node = nodesToInsert[id].node; - var details = nodesToInsert[id].details; - node.expanded = true; - } - - this._updateFilterStatus(); - }, - - _clear: function() - { - this._hiddenByFilterCount = 0; - this._updateFilterStatus(); - this._promiseIdToNode.clear(); - this._hidePopover(); - this._dataGrid.rootNode().removeChildren(); - this._linkifier.reset(); - }, - - /** - * @param {!WebInspector.ContextMenu} contextMenu - * @param {!WebInspector.DataGridNode} node - */ - _onContextMenu: function(contextMenu, node) - { - var debuggerModel = this._debuggerModel; - if (!debuggerModel) - return; - - var promiseId = node.promiseId(); - if (this._promiseDetailsByDebuggerModel.has(debuggerModel)) { - var details = this._promiseDetailsByDebuggerModel.get(debuggerModel).get(promiseId); - if (details.isGarbageCollected) - return; - } - - contextMenu.appendItem(WebInspector.UIString.capitalize("Show in ^console"), showPromiseInConsole); - contextMenu.show(); - - function showPromiseInConsole() - { - debuggerModel.getPromiseById(promiseId, "console", didGetPromiseById); - } - - /** - * @param {?RuntimeAgent.RemoteObject} promise - */ - function didGetPromiseById(promise) - { - if (!promise) - return; - var object = debuggerModel.target().runtimeModel.createRemoteObject(promise); - object.callFunction(dumpIntoConsole); - object.release(); - /** - * @suppressReceiverCheck - * @this {Object} - */ - function dumpIntoConsole() - { - console.log(this); - } - WebInspector.console.show(); - } - }, - - /** - * @param {!Element} element - * @param {!Event} event - * @return {!Element|!AnchorBox|undefined} - */ - _getPopoverAnchor: function(element, event) - { - if (!this._debuggerModel || !this._promiseDetailsByDebuggerModel.has(this._debuggerModel)) - return undefined; - var node = this._dataGrid.dataGridNodeFromNode(element); - if (!node) - return undefined; - var details = this._promiseDetailsByDebuggerModel.get(this._debuggerModel).get(node.promiseId()); - if (!details) - return undefined; - var anchor = element.enclosingNodeOrSelfWithClass("created-column"); - if (anchor) - return details.creationStack ? anchor : undefined; - anchor = element.enclosingNodeOrSelfWithClass("settled-column"); - return (anchor && details.settlementStack) ? anchor : undefined; - }, - - /** - * @param {!Element} anchor - * @param {!WebInspector.Popover} popover - */ - _showPopover: function(anchor, popover) - { - var node = this._dataGrid.dataGridNodeFromNode(anchor); - var details = this._promiseDetailsByDebuggerModel.get(this._debuggerModel).get(node.promiseId()); - - var stackTrace; - if (anchor.classList.contains("created-column")) - stackTrace = details.creationStack; - else - stackTrace = details.settlementStack; - - var content = WebInspector.DOMPresentationUtils.buildStackTracePreviewContents(this._debuggerModel.target(), this._linkifier, stackTrace); - popover.setCanShrink(true); - popover.showForAnchor(content, anchor); - }, - - __proto__: WebInspector.VBox.prototype -} - -/** - * @constructor - * @extends {WebInspector.ViewportDataGridNode} - * @param {!WebInspector.PromiseDetails} details - * @param {!WebInspector.DebuggerModel} debuggerModel - * @param {!WebInspector.Linkifier} linkifier - * @param {!WebInspector.ViewportDataGrid} dataGrid - */ -WebInspector.PromiseDataGridNode = function(details, debuggerModel, linkifier, dataGrid) -{ - WebInspector.ViewportDataGridNode.call(this, {}); - this._details = details; - this._debuggerModel = debuggerModel; - this._linkifier = linkifier; - /** @type {!Array.} */ - this._linkifiedAnchors = []; - this.dataGrid = dataGrid; -} - -WebInspector.PromiseDataGridNode.prototype = { - _disposeAnchors: function() - { - for (var i = 0; i < this._linkifiedAnchors.length; ++i) - this._linkifier.disposeAnchor(this._debuggerModel.target(), this._linkifiedAnchors[i]); - this._linkifiedAnchors = []; - }, - - /** - * @param {!WebInspector.PromiseDetails} details - */ - update: function(details) - { - this._disposeAnchors(); - this._details = details; - this.refresh(); - }, - - /** - * @override - */ - wasDetached: function() - { - this._disposeAnchors(); - }, - - /** - * @override - * @return {number} - */ - nodeSelfHeight: function() - { - return 24; - }, - - /** - * @return {number} - */ - promiseId: function() - { - return this._details.id; - }, - - /** - * @override - */ - createCells: function() - { - this._element.classList.toggle("promise-gc", !!this._details.isGarbageCollected); - WebInspector.ViewportDataGridNode.prototype.createCells.call(this); - }, - - /** - * @param {!Element} cell - * @param {?RuntimeAgent.CallFrame=} callFrame - */ - _appendCallFrameAnchor: function(cell, callFrame) - { - if (!callFrame) - return; - var anchor = this._linkifier.linkifyConsoleCallFrame(this._debuggerModel.target(), callFrame); - this._linkifiedAnchors.push(anchor); - cell.appendChild(anchor); - }, - - /** - * @override - * @param {string} columnIdentifier - * @return {!Element} - */ - createCell: function(columnIdentifier) - { - var cell = this.createTD(columnIdentifier); - var details = this._details; - - switch (columnIdentifier) { - case "status": - var title = ""; - switch (details.status) { - case "pending": - title = WebInspector.UIString("Pending"); - break; - case "resolved": - title = WebInspector.UIString("Fulfilled"); - break; - case "rejected": - title = WebInspector.UIString("Rejected"); - break; - } - if (details.isGarbageCollected) - title += " " + WebInspector.UIString("(garbage collected)"); - cell.createChild("div", "status " + details.status).title = title; - break; - - case "function": - cell.createTextChild(WebInspector.beautifyFunctionName(details.callFrame ? details.callFrame.functionName : "")); - break; - - case "created": - this._appendCallFrameAnchor(cell, details.callFrame); - break; - - case "settled": - this._appendCallFrameAnchor(cell, details.settlementStack && details.settlementStack.callFrames.length ? details.settlementStack.callFrames[0] : null); - break; - - case "tts": - cell.createTextChild(this._ttsCellText()); - break; - } - - return cell; - }, - - /** - * @return {string} - */ - _ttsCellText: function() - { - var details = this._details; - if (details.creationTime && details.settlementTime && details.settlementTime >= details.creationTime) - return Number.millisToString(details.settlementTime - details.creationTime); - return ""; - }, - - /** - * @param {?RuntimeAgent.CallFrame=} callFrame - * @return {string} - */ - _callFrameAnchorTextForSearch: function(callFrame) - { - if (!callFrame) - return ""; - var script = callFrame.scriptId && this._debuggerModel ? this._debuggerModel.scriptForId(callFrame.scriptId) : null; - var sourceURL = script ? script.sourceURL : callFrame.url; - var lineNumber = callFrame.lineNumber || 0; - return WebInspector.displayNameForURL(sourceURL) + ":" + lineNumber; - }, - - /** - * @return {string} - */ - dataTextForSearch: function() - { - var details = this._details; - var texts = [ - WebInspector.beautifyFunctionName(details.callFrame ? details.callFrame.functionName : ""), - this._callFrameAnchorTextForSearch(details.callFrame), - this._callFrameAnchorTextForSearch(details.settlementStack && details.settlementStack.callFrames.length ? details.settlementStack.callFrames[0] : null), - this._ttsCellText().replace(/\u2009/g, " ") // \u2009 is a thin space. - ]; - return texts.join(" "); - }, - - __proto__: WebInspector.ViewportDataGridNode.prototype -} diff --git a/front_end/promises/module.json b/front_end/promises/module.json deleted file mode 100644 index 520e4f6a64..0000000000 --- a/front_end/promises/module.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extensions": [ - { - "type": "drawer-view", - "name": "promises", - "title": "Promises", - "order": 30, - "persistence": "permanent", - "className": "WebInspector.PromisePane" - } - ], - "dependencies": ["components", "ui_lazy"], - "experiment": "promiseTracker", - "scripts": [ - "PromisePane.js" - ], - "resources": [ - "promisePane.css" - ] -} diff --git a/front_end/promises/promisePane.css b/front_end/promises/promisePane.css deleted file mode 100644 index 55a61a6998..0000000000 --- a/front_end/promises/promisePane.css +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2014 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -.promises .data-grid { - border: none; - flex: 1 1; -} - -.promises > .toolbar { - border-bottom: 1px solid #dadada; -} - -.promises .promise-gc { - opacity: 0.6; -} - -.promises .data-grid th:hover { - background-color: inherit !important; -} - -.promises .data-grid .odd { - background-color: #eee; -} - -.promises .data-grid .header-container { - height: 30px; -} - -.promises .data-grid .data-container { - top: 29px; -} - -.promises .data-grid table.data { - background: transparent; -} - -.promises .data-grid th { - background-color: white; -} - -.promises .data-grid td { - line-height: 17px; - height: 24px; - vertical-align: middle; -} - -.promises .data-grid th, -.promises .data-grid td { - border-bottom: 1px solid rgb(205, 205, 205); - border-left: 1px solid rgb(205, 205, 205); -} - -.promises .status { - -webkit-mask-image: url(Images/toolbarButtonGlyphs.png); - -webkit-mask-size: 352px 168px; - -webkit-mask-position: -294px -26px; - background-color: #bbb; - height: 20px; - width: 20px; -} - -@media (-webkit-min-device-pixel-ratio: 1.5) { -.promises .status { - -webkit-mask-image: url(Images/toolbarButtonGlyphs_2x.png); -} -} /* media */ - -.promises .status.rejected { - background-color: rgb(216, 0, 0); -} - -.promises .status.resolved { - background-color: #696; -} - -.promises-filters-header { - flex: 0 0 23px; - overflow: hidden; -} - -.promises-filter-status { - flex: 0 0 23px; - padding-left: 18px; - color: rgb(128, 128, 128); - font-style: italic; -} -.promises-filter-status .link:hover { - color: rgb(15%, 15%, 15%); -} -.promises-filter-status .link { - color: rgb(33%, 33%, 33%); -} diff --git a/front_end/resources/ServiceWorkersView.js b/front_end/resources/ServiceWorkersView.js index 23c0f89f20..2aafa6f038 100644 --- a/front_end/resources/ServiceWorkersView.js +++ b/front_end/resources/ServiceWorkersView.js @@ -15,10 +15,10 @@ WebInspector.ServiceWorkersView = function() /** @type {!Set.} */ this._securityOriginHosts = new Set(); - /** @type {!Map.} */ - this._originHostToOriginElementMap = new Map(); - /** @type {!Map.} */ - this._registrationIdToOriginElementMap = new Map(); + /** @type {!Map.} */ + this._originHostToOriginWidgetMap = new Map(); + /** @type {!Map.} */ + this._registrationIdToOriginWidgetMap = new Map(); var settingsDiv = createElementWithClass("div", "service-workers-settings"); var debugOnStartCheckboxLabel = createCheckboxLabel(WebInspector.UIString("Open DevTools window and pause JavaScript execution on Service Worker startup for debugging.")); @@ -28,7 +28,7 @@ WebInspector.ServiceWorkersView = function() settingsDiv.appendChild(debugOnStartCheckboxLabel); this.contentElement.appendChild(settingsDiv); - this._root = this.contentElement.createChild("ol"); + this._root = this.contentElement.createChild("div"); this._root.classList.add("service-workers-root"); WebInspector.targetManager.observeTargets(this); @@ -48,7 +48,6 @@ WebInspector.ServiceWorkersView.prototype = { this._debugOnStartCheckbox.disabled = false; this._debugOnStartCheckbox.checked = this._manager.debugOnStart(); - for (var registration of this._manager.registrations().values()) this._updateRegistration(registration); @@ -91,15 +90,15 @@ WebInspector.ServiceWorkersView.prototype = { if (!parsedURL) return; var originHost = parsedURL.host; - var originElement = this._originHostToOriginElementMap.get(originHost); - if (!originElement) { - originElement = new WebInspector.ServiceWorkerOriginElement(this._manager, originHost); + var originWidget = this._originHostToOriginWidgetMap.get(originHost); + if (!originWidget) { + originWidget = new WebInspector.ServiceWorkerOriginWidget(this._manager, originHost); if (this._securityOriginHosts.has(originHost)) - this._appendOriginNode(originElement); - this._originHostToOriginElementMap.set(originHost, originElement); + originWidget.show(this._root); + this._originHostToOriginWidgetMap.set(originHost, originWidget); } - this._registrationIdToOriginElementMap.set(registration.id, originElement); - originElement._updateRegistration(registration); + this._registrationIdToOriginWidgetMap.set(registration.id, originWidget); + originWidget._updateRegistration(registration); }, /** @@ -109,16 +108,16 @@ WebInspector.ServiceWorkersView.prototype = { { var registration = /** @type {!WebInspector.ServiceWorkerRegistration} */ (event.data); var registrationId = registration.id; - var originElement = this._registrationIdToOriginElementMap.get(registrationId); - if (!originElement) + var originWidget = this._registrationIdToOriginWidgetMap.get(registrationId); + if (!originWidget) return; - this._registrationIdToOriginElementMap.delete(registrationId); - originElement._deleteRegistration(registrationId); - if (originElement._hasRegistration()) + this._registrationIdToOriginWidgetMap.delete(registrationId); + originWidget._deleteRegistration(registrationId); + if (originWidget._hasRegistration()) return; - if (this._securityOriginHosts.has(originElement._originHost)) - this._removeOriginNode(originElement); - this._originHostToOriginElementMap.delete(originElement._originHost); + if (this._securityOriginHosts.has(originWidget._originHost)) + originWidget.detach(); + this._originHostToOriginWidgetMap.delete(originWidget._originHost); }, /** @@ -150,10 +149,10 @@ WebInspector.ServiceWorkersView.prototype = { if (this._securityOriginHosts.has(originHost)) return; this._securityOriginHosts.add(originHost); - var originElement = this._originHostToOriginElementMap.get(originHost); - if (!originElement) + var originWidget = this._originHostToOriginWidgetMap.get(originHost); + if (!originWidget) return; - this._appendOriginNode(originElement); + originWidget.show(this._root); }, /** @@ -169,26 +168,10 @@ WebInspector.ServiceWorkersView.prototype = { if (!this._securityOriginHosts.has(originHost)) return; this._securityOriginHosts.delete(originHost); - var originElement = this._originHostToOriginElementMap.get(originHost); - if (!originElement) + var originWidget = this._originHostToOriginWidgetMap.get(originHost); + if (!originWidget) return; - this._removeOriginNode(originElement); - }, - - /** - * @param {!WebInspector.ServiceWorkerOriginElement} originElement - */ - _appendOriginNode: function(originElement) - { - this._root.appendChild(originElement._element); - }, - - /** - * @param {!WebInspector.ServiceWorkerOriginElement} originElement - */ - _removeOriginNode: function(originElement) - { - this._root.removeChild(originElement._element); + originWidget.detach(); }, _debugOnStartCheckboxChanged: function() @@ -204,28 +187,28 @@ WebInspector.ServiceWorkersView.prototype = { /** * @constructor + * @extends {WebInspector.VBox} * @param {!WebInspector.ServiceWorkerManager} manager * @param {string} originHost */ -WebInspector.ServiceWorkerOriginElement = function(manager, originHost) +WebInspector.ServiceWorkerOriginWidget = function(manager, originHost) { + WebInspector.VBox.call(this); this._manager = manager; - /** @type {!Map.} */ - this._registrationElements = new Map(); + /** @type {!Map.} */ + this._registrationWidgets = new Map(); this._originHost = originHost; - this._element = createElementWithClass("div", "service-workers-origin"); - this._listItemNode = this._element.createChild("li", "service-workers-origin-title"); - this._listItemNode.createChild("div").setTextAndTitle(originHost); - this._childrenListNode = this._element.createChild("ol"); + this.element.classList.add("service-workers-origin"); + this._titleElement = this.element.createChild("span", "service-workers-origin-title"); } -WebInspector.ServiceWorkerOriginElement.prototype = { +WebInspector.ServiceWorkerOriginWidget.prototype = { /** * @return {boolean} */ _hasRegistration: function() { - return this._registrationElements.size != 0; + return this._registrationWidgets.size != 0; }, /** @@ -233,14 +216,16 @@ WebInspector.ServiceWorkerOriginElement.prototype = { */ _updateRegistration: function(registration) { - var swRegistrationElement = this._registrationElements.get(registration.id); - if (swRegistrationElement) { - swRegistrationElement._updateRegistration(registration); + this._titleElement.setTextAndTitle(WebInspector.UIString(registration.isDeleted ? "%s%s - deleted" : "%s%s", this._originHost, registration.scopeURL.asParsedURL().path)); + + var registrationWidget = this._registrationWidgets.get(registration.id); + if (registrationWidget) { + registrationWidget._updateRegistration(registration); return; } - swRegistrationElement = new WebInspector.SWRegistrationElement(this._manager, this, registration); - this._registrationElements.set(registration.id, swRegistrationElement); - this._childrenListNode.appendChild(swRegistrationElement._element); + registrationWidget = new WebInspector.SWRegistrationWidget(this._manager, this, registration); + this._registrationWidgets.set(registration.id, registrationWidget); + registrationWidget.show(this.element); }, /** @@ -248,187 +233,125 @@ WebInspector.ServiceWorkerOriginElement.prototype = { */ _deleteRegistration: function(registrationId) { - var swRegistrationElement = this._registrationElements.get(registrationId); - if (!swRegistrationElement) + var registrationWidget = this._registrationWidgets.get(registrationId); + if (!registrationWidget) return; - this._registrationElements.delete(registrationId); - this._childrenListNode.removeChild(swRegistrationElement._element); + this._registrationWidgets.delete(registrationId); + registrationWidget.detach(); }, - /** - * @return {boolean} - */ - _visible: function() - { - return !!this._element.parentElement; - }, + __proto__: WebInspector.VBox.prototype } /** * @constructor + * @extends {WebInspector.VBox} * @param {!WebInspector.ServiceWorkerManager} manager - * @param {!WebInspector.ServiceWorkerOriginElement} originElement + * @param {!WebInspector.ServiceWorkerOriginWidget} originWidget * @param {!WebInspector.ServiceWorkerRegistration} registration */ -WebInspector.SWRegistrationElement = function(manager, originElement, registration) +WebInspector.SWRegistrationWidget = function(manager, originWidget, registration) { + WebInspector.VBox.call(this); this._manager = manager; - this._originElement = originElement; + this._originWidget = originWidget; this._registration = registration; - this._element = createElementWithClass("div", "service-workers-registration"); - var headerNode = this._element.createChild("div", "service-workers-registration-header"); - this._titleNode = headerNode.createChild("div", "service-workers-registration-title"); - var buttonsNode = headerNode.createChild("div", "service-workers-registration-buttons"); - this._updateButton = buttonsNode.createChild("button", "service-workers-button service-workers-update-button"); - this._updateButton.addEventListener("click", this._updateButtonClicked.bind(this), false); - this._updateButton.title = WebInspector.UIString("Update"); - this._updateButton.disabled = true - this._pushButton = buttonsNode.createChild("button", "service-workers-button service-workers-push-button"); - this._pushButton.addEventListener("click", this._pushButtonClicked.bind(this), false); - this._pushButton.title = WebInspector.UIString("Emulate push event"); - this._pushButton.disabled = true - this._deleteButton = buttonsNode.createChild("button", "service-workers-button service-workers-delete-button"); - this._deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this), false); - this._deleteButton.title = WebInspector.UIString("Delete"); - this._childrenListNode = this._element.createChild("div", "service-workers-registration-content"); + this.element.classList.add("service-workers-registration"); + + var toolbar = new WebInspector.Toolbar("", this.element); + this._updateButton = new WebInspector.ToolbarButton(WebInspector.UIString("Update"), "refresh-toolbar-item", WebInspector.UIString("Update")); + this._updateButton.addEventListener("click", this._updateButtonClicked.bind(this)); + toolbar.appendToolbarItem(this._updateButton); + + toolbar.appendSeparator(); + this._pushButton = new WebInspector.ToolbarButton(WebInspector.UIString("Emulate push event"), "notification-toolbar-item", WebInspector.UIString("Push")); + this._pushButton.addEventListener("click", this._pushButtonClicked.bind(this)); + toolbar.appendToolbarItem(this._pushButton); + toolbar.appendSpacer(); + this._deleteButton = new WebInspector.ToolbarButton(WebInspector.UIString("Delete"), "garbage-collect-toolbar-item", WebInspector.UIString("Delete")); + this._deleteButton.addEventListener("click", this._deleteButtonClicked.bind(this)); + toolbar.appendToolbarItem(this._deleteButton); + + this._tabbedPane = new WebInspector.TabbedPane(); + this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSelected, this._tabSelected, this); + var modes = WebInspector.ServiceWorkerVersion.Modes; + this._tabbedPane.appendTab(modes.Installing, WebInspector.UIString("Installing"), new WebInspector.VBox()); + this._tabbedPane.appendTab(modes.Waiting, WebInspector.UIString("Waiting"), new WebInspector.VBox()); + this._tabbedPane.appendTab(modes.Active, WebInspector.UIString("Active"), new WebInspector.VBox()); + this._tabbedPane.appendTab(modes.Redundant, WebInspector.UIString("Redundant"), new WebInspector.VBox()); + this._tabbedPane.show(this.element); + + /** @type {!Map} */ + this._versionWidgets = new Map(); - /** - * @type {!Object.>} - */ - this._categorizedVersions = {}; - for (var mode in WebInspector.ServiceWorkerVersion.Modes) - this._categorizedVersions[WebInspector.ServiceWorkerVersion.Modes[mode]] = []; - - this._selectedMode = WebInspector.ServiceWorkerVersion.Modes.Active; + this._updateRegistration(registration); +} +WebInspector.SWRegistrationWidget.prototype = { /** - * @type {!Array.} + * @param {!WebInspector.Event} event */ - this._versionElements = []; - - this._updateRegistration(registration); -} + _tabSelected: function(event) + { + if (event.data["isUserGesture"]) + this._lastManuallySelectedTab = event.data["tabId"]; + }, -WebInspector.SWRegistrationElement.prototype = { /** * @param {!WebInspector.ServiceWorkerRegistration} registration */ _updateRegistration: function(registration) { this._registration = registration; - this._titleNode.setTextAndTitle(WebInspector.UIString(registration.isDeleted ? "Scope: %s - deleted" : "Scope: %s", registration.scopeURL.asParsedURL().path)); - this._updateButton.disabled = !!registration.isDeleted; - this._deleteButton.disabled = !!registration.isDeleted; - - var lastFocusedVersionId = undefined; - if (this._categorizedVersions[this._selectedMode].length) - lastFocusedVersionId = this._categorizedVersions[this._selectedMode][0].id; - for (var mode in WebInspector.ServiceWorkerVersion.Modes) - this._categorizedVersions[WebInspector.ServiceWorkerVersion.Modes[mode]] = []; + this._updateButton.setEnabled(!registration.isDeleted); + this._deleteButton.setEnabled(!registration.isDeleted); + + /** @type {!Map} */ + var versionWidgets = new Map(); + + var modesWithVersions = new Set(); + var firstMode; for (var version of registration.versions.valuesArray()) { if (version.isStoppedAndRedundant() && !version.errorMessages.length) continue; var mode = version.mode(); - this._categorizedVersions[mode].push(version); - if (version.id === lastFocusedVersionId) - this._selectedMode = mode; + if (!firstMode) + firstMode = mode; + modesWithVersions.add(mode); + var view = this._tabbedPane.tabView(mode); + var versionWidget = this._versionWidgets.get(version.id); + if (versionWidget) + versionWidget._updateVersion(version); + else + versionWidget = new WebInspector.SWVersionWidget(this._manager, this._registration.scopeURL, version); + versionWidget.show(view.element); + versionWidgets.set(version.id, versionWidget); } - if (!this._categorizedVersions[this._selectedMode].length) { - for (var mode of [WebInspector.ServiceWorkerVersion.Modes.Active, - WebInspector.ServiceWorkerVersion.Modes.Waiting, - WebInspector.ServiceWorkerVersion.Modes.Installing, - WebInspector.ServiceWorkerVersion.Modes.Redundant]) { - if (this._categorizedVersions[mode].length) { - this._selectedMode = mode; - break; - } - } + for (var id of this._versionWidgets.keys()) { + if (!versionWidgets.has(id)) + this._versionWidgets.get(id).detach(); } - this._pushButton.disabled = !this._categorizedVersions[WebInspector.ServiceWorkerVersion.Modes.Active].length || !!this._registration.isDeleted; + this._versionWidgets = versionWidgets; - this._updateVersionList(); - }, + for (var id of this._tabbedPane.tabIds()) + this._tabbedPane.setTabEnabled(id, modesWithVersions.has(id)); - _updateVersionList: function() - { - var fragment = createDocumentFragment(); - var modeTabList = createElementWithClass("div", "service-workers-versions-mode-tab-list"); - modeTabList.appendChild(this._createVersionModeTab(WebInspector.ServiceWorkerVersion.Modes.Installing)); - modeTabList.appendChild(this._createVersionModeTab(WebInspector.ServiceWorkerVersion.Modes.Waiting)); - modeTabList.appendChild(this._createVersionModeTab(WebInspector.ServiceWorkerVersion.Modes.Active)); - modeTabList.appendChild(this._createVersionModeTab(WebInspector.ServiceWorkerVersion.Modes.Redundant)); - fragment.appendChild(modeTabList); - fragment.appendChild(this._createSelectedModeVersionsPanel(this._selectedMode)); - this._childrenListNode.removeChildren(); - this._childrenListNode.appendChild(fragment); - }, - - /** - * @param {string} mode - * @return {!Element} - */ - _createVersionModeTab: function(mode) - { - var versions = this._categorizedVersions[mode]; - var modeTitle = WebInspector.UIString(mode); - var selected = this._selectedMode == mode; - var modeTab = createElementWithClass("div", "service-workers-versions-mode-tab"); - for (var version of versions) { - var icon = modeTab.createChild("div", "service-workers-versions-mode-tab-icon service-workers-color-" + (version.id % 10)); - icon.title = WebInspector.UIString("ID: %s", version.id); - } - var modeTabText = modeTab.createChild("div", "service-workers-versions-mode-tab-text"); - modeTabText.setTextAndTitle(WebInspector.UIString(modeTitle)); - if (selected) { - modeTab.classList.add("service-workers-versions-mode-tab-selected"); - modeTabText.classList.add("service-workers-versions-mode-tab-text-selected"); - } - if (versions.length) { - modeTab.addEventListener("click", this._modeTabClicked.bind(this, mode), false); - } else { - modeTab.classList.add("service-workers-versions-mode-tab-disabled"); - modeTabText.classList.add("service-workers-versions-mode-tab-text-disabled"); - } - return modeTab; - }, + this._pushButton.setEnabled(modesWithVersions.has(WebInspector.ServiceWorkerVersion.Modes.Active) && !this._registration.isDeleted); - /** - * @param {string} mode - * @return {!Element} - */ - _createSelectedModeVersionsPanel: function(mode) - { - var versions = this._categorizedVersions[mode]; - var panelContainer = createElementWithClass("div", "service-workers-versions-panel-container"); - var index = 0; - var versionElement; - for (var i = 0; i < versions.length; ++i) { - if (i < this._versionElements.length) { - versionElement = this._versionElements[i]; - versionElement._updateVersion(versions[i]); - } else { - versionElement = new WebInspector.SWVersionElement(this._manager, this._registration.scopeURL, versions[i]); - this._versionElements.push(versionElement); - } - panelContainer.appendChild(versionElement._element); + if (modesWithVersions.has(this._lastManuallySelectedTab)) { + this._tabbedPane.selectTab(this._lastManuallySelectedTab); + return; } - this._versionElements.splice(versions.length); - return panelContainer; - }, - - /** - * @param {string} mode - */ - _modeTabClicked: function(mode) - { - if (this._selectedMode == mode) + if (modesWithVersions.has(WebInspector.ServiceWorkerVersion.Modes.Active)) { + this._tabbedPane.selectTab(WebInspector.ServiceWorkerVersion.Modes.Active); return; - this._selectedMode = mode; - this._updateVersionList(); + } + if (firstMode) + this._tabbedPane.selectTab(firstMode); }, /** - * @param {!Event} event + * @param {!WebInspector.Event} event */ _deleteButtonClicked: function(event) { @@ -436,7 +359,7 @@ WebInspector.SWRegistrationElement.prototype = { }, /** - * @param {!Event} event + * @param {!WebInspector.Event} event */ _updateButtonClicked: function(event) { @@ -444,7 +367,7 @@ WebInspector.SWRegistrationElement.prototype = { }, /** - * @param {!Event} event + * @param {!WebInspector.Event} event */ _pushButtonClicked: function(event) { @@ -452,27 +375,23 @@ WebInspector.SWRegistrationElement.prototype = { this._manager.deliverPushMessage(this._registration.id, data); }, - /** - * @return {boolean} - */ - _visible: function() - { - return this._originElement._visible(); - }, + __proto__: WebInspector.VBox.prototype } /** * @constructor + * @extends {WebInspector.VBox} * @param {!WebInspector.ServiceWorkerManager} manager * @param {string} scopeURL * @param {!WebInspector.ServiceWorkerVersion} version */ -WebInspector.SWVersionElement = function(manager, scopeURL, version) +WebInspector.SWVersionWidget = function(manager, scopeURL, version) { + WebInspector.VBox.call(this); this._manager = manager; this._scopeURL = scopeURL; this._version = version; - this._element = createElementWithClass("div", "service-workers-version"); + this.element.classList.add("service-workers-version", "flex-none"); /** * @type {!Object.} @@ -482,7 +401,7 @@ WebInspector.SWVersionElement = function(manager, scopeURL, version) this._updateVersion(version); } -WebInspector.SWVersionElement.prototype = { +WebInspector.SWVersionWidget.prototype = { _createElements: function() { var panel = createElementWithClass("div", "service-workers-versions-panel"); @@ -500,7 +419,7 @@ WebInspector.SWVersionElement.prototype = { this._clientsTitle = rightPanel.createChild("div", "service-workers-versions-table-clients-title"); this._clientsTitle.createTextChild(WebInspector.UIString("Controlled clients")); this._clientsPanel = rightPanel.createChild("div", "service-workers-versions-table-clients-content"); - this._element.appendChild(panel); + this.element.appendChild(panel); }, /** @@ -516,13 +435,15 @@ WebInspector.SWVersionElement.prototype = { var runningStatusLeftCell = runningStatusCell.createChild("div", "service-workers-versions-table-running-status-left-cell"); var runningStatusRightCell = runningStatusCell.createChild("div", "service-workers-versions-table-running-status-right-cell"); if (version.isRunning() || version.isStarting()) { - var stopButton = runningStatusLeftCell.createChild("button", "service-workers-button service-workers-stop-button"); - stopButton.addEventListener("click", this._stopButtonClicked.bind(this, version.id), false); - stopButton.title = WebInspector.UIString("Stop"); + var toolbar = new WebInspector.Toolbar("", runningStatusLeftCell); + var stopButton = new WebInspector.ToolbarButton(WebInspector.UIString("Stop"), "stop-toolbar-item"); + stopButton.addEventListener("click", this._stopButtonClicked.bind(this, version.id)); + toolbar.appendToolbarItem(stopButton); } else if (version.isStartable()) { - var startButton = runningStatusLeftCell.createChild("button", "service-workers-button service-workers-start-button"); - startButton.addEventListener("click", this._startButtonClicked.bind(this), false); - startButton.title = WebInspector.UIString("Start"); + var toolbar = new WebInspector.Toolbar("", runningStatusLeftCell); + var startButton = new WebInspector.ToolbarButton(WebInspector.UIString("Start"), "play-toolbar-item"); + startButton.addEventListener("click", this._startButtonClicked.bind(this)); + toolbar.appendToolbarItem(startButton); } runningStatusRightCell.setTextAndTitle(version.runningStatus); if (version.isRunning() || version.isStarting()) { @@ -627,7 +548,7 @@ WebInspector.SWVersionElement.prototype = { }, /** - * @param {!Event} event + * @param {!WebInspector.Event} event */ _startButtonClicked: function(event) { @@ -636,7 +557,7 @@ WebInspector.SWVersionElement.prototype = { /** * @param {string} versionId - * @param {!Event} event + * @param {!WebInspector.Event} event */ _stopButtonClicked: function(versionId, event) { @@ -651,4 +572,6 @@ WebInspector.SWVersionElement.prototype = { { this._manager.inspectWorker(versionId); }, + + __proto__: WebInspector.VBox.prototype } diff --git a/front_end/resources/module.json b/front_end/resources/module.json index 29dc86bf50..dc773577fc 100644 --- a/front_end/resources/module.json +++ b/front_end/resources/module.json @@ -11,6 +11,12 @@ "type": "@WebInspector.Revealer", "contextTypes": ["WebInspector.Resource"], "className": "WebInspector.ResourcesPanel.ResourceRevealer" + }, + { + "type": "setting", + "settingName": "serviceWorkerUpdateOnReload", + "settingType": "boolean", + "defaultValue": "false" } ], "dependencies": ["source_frame", "ui_lazy", "components_lazy"], diff --git a/front_end/resources/serviceWorkersView.css b/front_end/resources/serviceWorkersView.css index c7fa7e58b0..a308604b05 100644 --- a/front_end/resources/serviceWorkersView.css +++ b/front_end/resources/serviceWorkersView.css @@ -4,27 +4,25 @@ * found in the LICENSE file. */ -.service-workers-view { - overflow: auto; -} - .service-workers-settings { border-bottom: 1px solid #e1e1e1; min-height: 26px; overflow: hidden; padding: 5px; white-space: nowrap; + flex: none; } .service-workers-root { margin: 0; padding: 5px; -webkit-user-select: text; + display: flex; } .service-workers-root ol { list-style-type: none; - padding-left: 12px; + padding-left: 0; } .service-workers-origin { @@ -32,23 +30,23 @@ } .service-workers-origin-title { - font-size: 20px; + font-size: 17px; + flex: none; } .service-workers-registration { - border-right: 1px solid #e1e1e1; margin-top: 5px; margin-bottom: 10px; padding: 0; + flex: auto; } -.service-workers-registration-header { - border-left: 1px solid #e1e1e1; - border-top: 1px solid #e1e1e1; - display: flex; - flex-wrap: wrap; - overflow: hidden; - position: relative; +.service-workers-registration .tabbed-pane > .widget { + overflow: auto; +} + +.service-workers-registration .toolbar { + flex: none; } .service-workers-registration-title { @@ -57,62 +55,10 @@ padding: 4px; } -.service-workers-registration-buttons { - flex: 1 1 0; - padding: 1px; -} - -.service-workers-registration-content { - border: 0; - padding: 0; -} - -.service-workers-versions-mode-tab-list { - display: flex; -} - -.service-workers-versions-mode-tab { - border-bottom: 1px solid #e1e1e1; - border-left: 1px solid #e1e1e1; - border-top: 1px solid #e1e1e1; - display: flex; - flex: 1 1 0; - flex-wrap: wrap; - overflow: hidden; - padding: 3px; -} - -.service-workers-versions-mode-tab-disabled { - background-color: #eee; -} - -.service-workers-versions-mode-tab-selected { - border-bottom: 0; -} - -.service-workers-versions-mode-tab-icon { - height: 16px; - margin: 1px; - -webkit-mask-image: url(Images/serviceWorker.svg); - -webkit-mask-size: 16px 16px; - width: 16px; -} - -.service-workers-versions-mode-tab-text { - margin: 2px; -} - -.service-workers-versions-mode-tab-text-disabled { - color: #888; -} - -.service-workers-versions-mode-tab-text-selected { - font-weight: bold; -} - .service-workers-versions-panel { border-bottom: 1px solid #e1e1e1; border-left: 1px solid #e1e1e1; + border-right: 1px solid #e1e1e1; display: flex; } @@ -151,8 +97,12 @@ } .service-workers-versions-table-messages-content { - overflow: auto; padding: 3px 3px 3px 10px; + line-height: 22px; + max-height: 150px; + border-left: 1px solid #eee; + overflow: auto; + margin-right: 10px; } .service-workers-versions-table-clients-title { @@ -179,63 +129,6 @@ text-decoration: underline; } -.service-workers-button { - -webkit-mask-image: url(Images/toolbarButtonGlyphs.png); - -webkit-mask-size: 352px 168px; - background-color: rgba(0, 0, 0, 0.75); - height: 24px; - opacity: 0.8; - width: 32px; - border: 0; - padding: 0; -} - -@media (-webkit-min-device-pixel-ratio: 1.5) { -.service-workers-button { - -webkit-mask-image: url(Images/toolbarButtonGlyphs_2x.png); -} -} /* media */ - -.service-workers-button:hover { - opacity: 1; -} - -.service-workers-button:active { - opacity: 0.8; -} - -.service-workers-button:disabled { - opacity: 0.5; -} - -.service-workers-start-button { - -webkit-mask-position: -64px -48px; -} - -.service-workers-stop-button { - -webkit-mask-position: -96px -48px; -} - -.service-workers-delete-button { - float: right; - -webkit-mask-position: -128px -24px; - position: relative; -} - -.service-workers-update-button { - -webkit-mask-image: url(Images/updateServiceWorker.svg); - -webkit-mask-position: 7px 3px; - -webkit-mask-repeat: no-repeat; - -webkit-mask-size: 18px 18px; -} - -.service-workers-push-button { - -webkit-mask-image: url(Images/notifications.svg); - -webkit-mask-position: 7px 3px; - -webkit-mask-repeat: no-repeat; - -webkit-mask-size: 18px 18px; -} - .service-workers-versions-option-panel { border-bottom: 1px solid #e1e1e1; border-left: 1px solid #e1e1e1; @@ -246,12 +139,13 @@ display: flex; } -.service-workers-error-message { - font-weight: bold; +.service-workers-error > label { + flex: none; } .service-workers-info { display: flex; + align-items: center; } .service-worker-client-focus { @@ -260,43 +154,3 @@ padding-left: 4px; text-decoration: underline; } - -.service-workers-color-0 { - background-color: #003366; -} - -.service-workers-color-1 { - background-color: #663300; -} - -.service-workers-color-2 { - background-color: #660066; -} - -.service-workers-color-3 { - background-color: #003300; -} - -.service-workers-color-4 { - background-color: #993333; -} - -.service-workers-color-5 { - background-color: #000066; -} - -.service-workers-color-6 { - background-color: #006666; -} - -.service-workers-color-7 { - background-color: #996600; -} - -.service-workers-color-8 { - background-color: #9900CC; -} - -.service-workers-color-9 { - background-color: #336600; -} diff --git a/front_end/sass/SASSProcessor.js b/front_end/sass/SASSProcessor.js index a747f0c431..62f1dae4c0 100644 --- a/front_end/sass/SASSProcessor.js +++ b/front_end/sass/SASSProcessor.js @@ -120,6 +120,8 @@ WebInspector.SASSProcessor.processCSSEdits = function(astService, map, ranges, n */ function onCSSParsed(newCSSAST) { + if (newCSSAST.rules.length !== map.compiledModel().rules.length) + return Promise.resolve(/** @type {?WebInspector.SourceMap.EditResult} */(null)); //TODO(lushnikov): only diff changed styles. var cssDiff = WebInspector.SASSSupport.diffModels(map.compiledModel(), newCSSAST); var edits = WebInspector.SASSProcessor._editsFromCSSDiff(cssDiff, map); diff --git a/front_end/sass/SASSSupport.js b/front_end/sass/SASSSupport.js index 92dad21eda..78e67d29d2 100644 --- a/front_end/sass/SASSSupport.js +++ b/front_end/sass/SASSSupport.js @@ -80,7 +80,6 @@ WebInspector.SASSSupport.SCSSParserStates = { */ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) { - var lines = document.text.value().split("\n"); var properties = []; var variables = []; var mixins = []; @@ -92,45 +91,50 @@ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) var mixinName, mixinValue; var UndefTokenType = {}; + var cursor = new WebInspector.TextCursor(document.text.lineEndings()); + /** * @param {string} tokenValue * @param {?string} tokenTypes - * @param {number} column - * @param {number} newColumn + * @param {number} startPosition + * @param {number} endPosition */ - function processToken(tokenValue, tokenTypes, column, newColumn) + function processToken(tokenValue, tokenTypes, startPosition, endPosition) { + cursor.advance(startPosition); + var startLine = cursor.lineNumber(); + var startColumn = cursor.columnNumber(); + cursor.advance(endPosition); + var endLine = cursor.lineNumber(); + var endColumn = cursor.columnNumber(); + var tokenType = tokenTypes ? tokenTypes.split(" ").keySet() : UndefTokenType; switch (state) { case States.Initial: if (tokenType["css-variable-2"]) { - variableName = new WebInspector.SASSSupport.TextNode(document, tokenValue, new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)); + variableName = new WebInspector.SASSSupport.TextNode(document, tokenValue, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn)); state = States.VariableName; } else if (tokenType["css-property"] || tokenType["css-meta"]) { - propertyName = new WebInspector.SASSSupport.TextNode(document, tokenValue, new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)); + propertyName = new WebInspector.SASSSupport.TextNode(document, tokenValue, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn)); state = States.PropertyName; } else if (tokenType["css-def"] && tokenValue === "@include") { - mixinName = new WebInspector.SASSSupport.TextNode(document, tokenValue, new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn)); + mixinName = new WebInspector.SASSSupport.TextNode(document, tokenValue, new WebInspector.TextRange(startLine, startColumn, endLine, endColumn)); state = States.MixinName; } else if (tokenType["css-comment"]) { // Support only a one-line comments. - if (tokenValue.substring(0, 2) !== "/*" || tokenValue.substring(tokenValue.length - 2) !== "*/") + if (startLine !== endLine || tokenValue.substring(0, 2) !== "/*" || tokenValue.substring(tokenValue.length - 2) !== "*/") break; var uncommentedText = tokenValue.substring(2, tokenValue.length - 2); - var fakeRuleText = "a{\n" + uncommentedText + "}"; + var fakeRuleText = "a{" + uncommentedText + "}"; var fakeDocument = new WebInspector.SASSSupport.ASTDocument("", new WebInspector.Text(fakeRuleText)); var result = WebInspector.SASSSupport._innerParseSCSS(fakeDocument, tokenizerFactory); if (result.properties.length === 1 && result.variables.length === 0 && result.mixins.length === 0) { var disabledProperty = result.properties[0]; - // We should offset property to current coordinates. - var offset = column + 2; - var nameRange = new WebInspector.TextRange(lineNumber, disabledProperty.name.range.startColumn + offset, - lineNumber, disabledProperty.name.range.endColumn + offset); - var valueRange = new WebInspector.TextRange(lineNumber, disabledProperty.value.range.startColumn + offset, - lineNumber, disabledProperty.value.range.endColumn + offset); + var nameRange = rebaseInsideOneLineComment(disabledProperty.name.range, startLine, startColumn); + var valueRange = rebaseInsideOneLineComment(disabledProperty.value.range, startLine, startColumn); var name = new WebInspector.SASSSupport.TextNode(document, disabledProperty.name.text, nameRange); var value = new WebInspector.SASSSupport.TextNode(document, disabledProperty.value.text, valueRange); - var range = new WebInspector.TextRange(lineNumber, column, lineNumber, newColumn); + var range = new WebInspector.TextRange(startLine, startColumn, startLine, endColumn); var property = new WebInspector.SASSSupport.Property(document, name, value, range, true); properties.push(property); } @@ -145,18 +149,18 @@ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) state = States.Initial; } else if (tokenValue === ":" && tokenType === UndefTokenType) { state = States.VariableValue; - variableValue = new WebInspector.SASSSupport.TextNode(document, "", WebInspector.TextRange.createFromLocation(lineNumber, newColumn)); + variableValue = new WebInspector.SASSSupport.TextNode(document, "", WebInspector.TextRange.createFromLocation(startLine, endColumn)); } else if (tokenType !== UndefTokenType) { state = States.Initial; } break; case States.VariableValue: if (tokenValue === ";" && tokenType === UndefTokenType) { - variableValue.range.endLine = lineNumber; - variableValue.range.endColumn = column; + variableValue.range.endLine = startLine; + variableValue.range.endColumn = startColumn; var variable = new WebInspector.SASSSupport.Property(document, variableName, variableValue, variableName.range.clone(), false); - variable.range.endLine = lineNumber; - variable.range.endColumn = newColumn; + variable.range.endLine = startLine; + variable.range.endColumn = endColumn; variables.push(variable); state = States.Initial; } else { @@ -164,22 +168,26 @@ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) } break; case States.PropertyName: - if (tokenValue === ":" && tokenType === UndefTokenType) { + if (tokenValue === "{" && tokenType === UndefTokenType) { + state = States.Initial; + } else if (tokenValue === ":" && tokenType === UndefTokenType) { state = States.PropertyValue; - propertyName.range.endLine = lineNumber; - propertyName.range.endColumn = column; - propertyValue = new WebInspector.SASSSupport.TextNode(document, "", WebInspector.TextRange.createFromLocation(lineNumber, newColumn)); + propertyName.range.endLine = startLine; + propertyName.range.endColumn = startColumn; + propertyValue = new WebInspector.SASSSupport.TextNode(document, "", WebInspector.TextRange.createFromLocation(startLine, endColumn)); } else if (tokenType["css-property"]) { propertyName.text += tokenValue; } break; case States.PropertyValue: - if ((tokenValue === "}" || tokenValue === ";") && tokenType === UndefTokenType) { - propertyValue.range.endLine = lineNumber; - propertyValue.range.endColumn = column; + if (tokenValue === "{" && tokenType === UndefTokenType) { + state = States.Initial; + } else if ((tokenValue === "}" || tokenValue === ";") && tokenType === UndefTokenType) { + propertyValue.range.endLine = startLine; + propertyValue.range.endColumn = startColumn; var property = new WebInspector.SASSSupport.Property(document, propertyName, propertyValue, propertyName.range.clone(), false); - property.range.endLine = lineNumber; - property.range.endColumn = newColumn; + property.range.endLine = startLine; + property.range.endColumn = endColumn; properties.push(property); state = States.Initial; } else { @@ -189,9 +197,9 @@ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) case States.MixinName: if (tokenValue === "(" && tokenType === UndefTokenType) { state = States.MixinValue; - mixinName.range.endLine = lineNumber; - mixinName.range.endColumn = column; - mixinValue = new WebInspector.SASSSupport.TextNode(document, "", WebInspector.TextRange.createFromLocation(lineNumber, newColumn)); + mixinName.range.endLine = startLine; + mixinName.range.endColumn = startColumn; + mixinValue = new WebInspector.SASSSupport.TextNode(document, "", WebInspector.TextRange.createFromLocation(startLine, endColumn)); } else if (tokenValue === ";" && tokenType === UndefTokenType) { state = States.Initial; mixinValue = null; @@ -201,11 +209,11 @@ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) break; case States.MixinValue: if (tokenValue === ")" && tokenType === UndefTokenType) { - mixinValue.range.endLine = lineNumber; - mixinValue.range.endColumn = column; + mixinValue.range.endLine = startLine; + mixinValue.range.endColumn = startColumn; var mixin = new WebInspector.SASSSupport.Property(document, mixinName, /** @type {!WebInspector.SASSSupport.TextNode} */(mixinValue), mixinName.range.clone(), false); - mixin.range.endLine = lineNumber; - mixin.range.endColumn = newColumn; + mixin.range.endLine = startLine; + mixin.range.endColumn = endColumn; mixins.push(mixin); state = States.Initial; } else { @@ -221,17 +229,24 @@ WebInspector.SASSSupport._innerParseSCSS = function(document, tokenizerFactory) } } var tokenizer = tokenizerFactory.createTokenizer("text/x-scss"); - var lineNumber; - for (lineNumber = 0; lineNumber < lines.length; ++lineNumber) { - var line = lines[lineNumber]; - tokenizer(line, processToken); - processToken("\n", null, line.length, line.length + 1); - } + tokenizer(document.text.value(), processToken); + return { variables: variables, properties: properties, mixins: mixins }; + + /** + * @param {!WebInspector.TextRange} range + * @param {number} startLine + * @param {number} startColumn + * @return {!WebInspector.TextRange} + */ + function rebaseInsideOneLineComment(range, startLine, startColumn) + { + return new WebInspector.TextRange(range.startLine + startLine, range.startColumn + startColumn, range.endLine + startLine, range.endColumn + startColumn); + } } /** diff --git a/front_end/sass/module.json b/front_end/sass/module.json index dc9deb47ad..b4983d27b6 100644 --- a/front_end/sass/module.json +++ b/front_end/sass/module.json @@ -6,5 +6,15 @@ "SASSProcessor.js", "ASTSourceMap.js", "SASSSourceMapFactory.js" + ], + "extensions": [ + { + "type": "@WebInspector.SourceMapFactory", + "className": "WebInspector.SASSSourceMapFactory", + "experiment": "liveSASS", + "extensions": [ + "scss" + ] + } ] } diff --git a/front_end/screencast/ScreencastApp.js b/front_end/screencast/ScreencastApp.js index b3f22741a3..8eb98047f4 100644 --- a/front_end/screencast/ScreencastApp.js +++ b/front_end/screencast/ScreencastApp.js @@ -90,12 +90,6 @@ WebInspector.ScreencastApp.prototype = { this._rootSplitWidget.showBoth(); else this._rootSplitWidget.hideMain(); - }, - - _requestAppBanner: function() - { - if (this._target && this._target.pageAgent()) - this._target.pageAgent().requestAppBanner(); } }; @@ -132,33 +126,6 @@ WebInspector.ScreencastApp.ToolbarButtonProvider.prototype = { } } - -/** - * @constructor - * @implements {WebInspector.ActionDelegate} - */ -WebInspector.ScreencastApp.ActionDelegate = function() -{ -}; - -WebInspector.ScreencastApp.ActionDelegate.prototype = { - /** - * @override - * @param {!WebInspector.Context} context - * @param {string} actionId - * @return {boolean} - */ - handleAction: function(context, actionId) - { - if (actionId === "screencast.request-app-banner") { - WebInspector.ScreencastApp._instance()._requestAppBanner() - return true; - } - return false; - } -}; - - /** * @constructor * @implements {WebInspector.AppProvider} diff --git a/front_end/screencast/module.json b/front_end/screencast/module.json index 0f9edda280..64f46d99e7 100644 --- a/front_end/screencast/module.json +++ b/front_end/screencast/module.json @@ -11,19 +11,12 @@ "order": 1, "location": "main-toolbar-left" }, - { - "type": "@WebInspector.ActionDelegate", - "actionId": "screencast.request-app-banner", - "className": "WebInspector.ScreencastApp.ActionDelegate", - "title": "Request app banner\u2026" - }, { "type": "context-menu-item", "condition": "remoteFrontend", "location": "mainMenu/tools", - "experiment": "appBanner", "order": 10, - "actionId": "screencast.request-app-banner" + "actionId": "components.request-app-banner" } ], "dependencies": [ diff --git a/front_end/sdk/CSSModel.js b/front_end/sdk/CSSModel.js index f1860be784..1cd81314e0 100644 --- a/front_end/sdk/CSSModel.js +++ b/front_end/sdk/CSSModel.js @@ -47,8 +47,9 @@ WebInspector.CSSModel = function(target) /** @type {!Map.>>} */ this._styleSheetIdsForURL = new Map(); - /** @type {!Map} */ - this._sourceMapLoadingPromises = new Map(); + /** @type {!Multimap} */ + this._sourceMapLoadingStyleSheetsIds = new Multimap(); + /** @type {!Map} */ this._sourceMapByURL = new Map(); /** @type {!Multimap} */ @@ -65,7 +66,8 @@ WebInspector.CSSModel.Events = { StyleSheetChanged: "StyleSheetChanged", StyleSheetRemoved: "StyleSheetRemoved", SourceMapAttached: "SourceMapAttached", - SourceMapDetached: "SourceMapDetached" + SourceMapDetached: "SourceMapDetached", + SourceMapChanged: "SourceMapChanged" } WebInspector.CSSModel.MediaTypes = ["all", "braille", "embossed", "handheld", "print", "projection", "screen", "speech", "tty", "tv"]; @@ -99,7 +101,7 @@ WebInspector.CSSModel.prototype = { if (enabled) this._attachSourceMap(header); else - this._detachSourceMap(header.sourceMapURL, header); + this._detachSourceMap(header); } }, @@ -112,6 +114,17 @@ WebInspector.CSSModel.prototype = { return this._sourceMapByURL.get(header.sourceMapURL) || null; }, + _sourceMapLoadedForTest: function() { }, + + /** + * @param {!WebInspector.SourceMap} sourceMap + * @return {!Array} + */ + headersForSourceMap: function(sourceMap) + { + return this._sourceMapURLToHeaders.get(sourceMap.url()).valuesArray(); + }, + /** * @param {!WebInspector.CSSStyleSheetHeader} header */ @@ -120,45 +133,104 @@ WebInspector.CSSModel.prototype = { var sourceMapURL = header.sourceMapURL; if (!sourceMapURL || !WebInspector.moduleSetting("cssSourceMapsEnabled").get()) return; - this._sourceMapURLToHeaders.set(sourceMapURL, header); if (this._sourceMapByURL.has(sourceMapURL)) { - this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapAttached, header); + attach.call(this, sourceMapURL, header); return; } - if (this._sourceMapLoadingPromises.has(sourceMapURL)) - return; - var loadingPromise = WebInspector.TextSourceMap.load(sourceMapURL, header.sourceURL) - .then(onSourceMapLoaded.bind(this, sourceMapURL)); - this._sourceMapLoadingPromises.set(sourceMapURL, loadingPromise); + if (!this._sourceMapLoadingStyleSheetsIds.has(sourceMapURL)) { + WebInspector.TextSourceMap.load(sourceMapURL, header.sourceURL) + .then(onTextSourceMapLoaded.bind(this, sourceMapURL)) + .then(onSourceMap.bind(this, sourceMapURL)); + } + this._sourceMapLoadingStyleSheetsIds.set(sourceMapURL, header.id); /** * @param {string} sourceMapURL * @param {?WebInspector.TextSourceMap} sourceMap + * @return {!Promise} * @this {WebInspector.CSSModel} */ - function onSourceMapLoaded(sourceMapURL, sourceMap) + function onTextSourceMapLoaded(sourceMapURL, sourceMap) { - this._sourceMapLoadingPromises.delete(sourceMapURL); - var headers = this._sourceMapURLToHeaders.get(sourceMapURL); - if (!headers || !sourceMap) + if (!sourceMap) + return Promise.resolve(/** @type {?WebInspector.SourceMap} */(null)); + var factoryExtension = this._factoryForSourceMap(sourceMap); + if (!factoryExtension) + return Promise.resolve(/** @type {?WebInspector.SourceMap} */(sourceMap)); + + return factoryExtension.instancePromise() + .then(factory => factory.editableSourceMap(this.target(), sourceMap)) + .then(map => map || sourceMap) + .catchException(/** @type {?WebInspector.SourceMap} */(null)); + } + + /** + * @param {string} sourceMapURL + * @param {?WebInspector.SourceMap} sourceMap + * @this {WebInspector.CSSModel} + */ + function onSourceMap(sourceMapURL, sourceMap) + { + this._sourceMapLoadedForTest(); + var styleSheetIds = this._sourceMapLoadingStyleSheetsIds.get(sourceMapURL); + this._sourceMapLoadingStyleSheetsIds.removeAll(sourceMapURL); + if (!sourceMap) return; + var headers = new Set(); + for (var styleSheetId of styleSheetIds) { + var header = this.styleSheetHeaderForId(styleSheetId); + if (header) + headers.add(header); + } + if (!headers.size) + return; + if (sourceMap.editable()) + WebInspector.console.log(WebInspector.UIString("LiveSASS started: %s", sourceMapURL)); this._sourceMapByURL.set(sourceMapURL, sourceMap); for (var header of headers) - this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapAttached, header); + attach.call(this, sourceMapURL, header); } + + /** + * @param {string} sourceMapURL + * @param {!WebInspector.CSSStyleSheetHeader} header + * @this {WebInspector.CSSModel} + */ + function attach(sourceMapURL, header) + { + this._sourceMapURLToHeaders.set(sourceMapURL, header); + this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapAttached, header); + } + }, + + /** + * @param {!WebInspector.SourceMap} sourceMap + * @return {?Runtime.Extension} + */ + _factoryForSourceMap: function(sourceMap) + { + var sourceExtensions = new Set(sourceMap.sourceURLs().map(url => WebInspector.TextUtils.extension(url))); + for (var runtimeExtension of self.runtime.extensions(WebInspector.SourceMapFactory)) { + var supportedExtensions = new Set(runtimeExtension.descriptor()["extensions"]); + if (supportedExtensions.containsAll(sourceExtensions)) + return runtimeExtension; + } + return null; }, /** - * @param {?string} sourceMapURL * @param {!WebInspector.CSSStyleSheetHeader} header */ - _detachSourceMap: function(sourceMapURL, header) + _detachSourceMap: function(header) { - if (!sourceMapURL) + if (!header.sourceMapURL || !this._sourceMapURLToHeaders.hasValue(header.sourceMapURL, header)) return; - this._sourceMapURLToHeaders.remove(sourceMapURL, header); - if (!this._sourceMapURLToHeaders.has(sourceMapURL)) - this._sourceMapByURL.delete(sourceMapURL); + this._sourceMapURLToHeaders.remove(header.sourceMapURL, header); + if (!this._sourceMapURLToHeaders.has(header.sourceMapURL)) + var sourceMap = this._sourceMapByURL.get(header.sourceMapURL); + if (sourceMap.editable()) + WebInspector.console.log(WebInspector.UIString("LiveSASS stopped: %s", header.sourceMapURL)); + this._sourceMapByURL.delete(header.sourceMapURL); this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapDetached, header); }, @@ -170,6 +242,119 @@ WebInspector.CSSModel.prototype = { return /** @type {!WebInspector.DOMModel} */(this._domModel); }, + /** + * @param {!CSSAgent.StyleSheetId} styleSheetId + * @param {!WebInspector.TextRange} range + * @param {string} text + * @param {boolean} majorChange + * @return {!Promise} + */ + setStyleText: function(styleSheetId, range, text, majorChange) + { + var original = this._innerSetStyleTexts.bind(this, [styleSheetId], [range], [text], majorChange); + var header = this.styleSheetHeaderForId(styleSheetId); + if (!header) + return original(); + + var sourceMap = this.sourceMapForHeader(header); + if (!sourceMap) + return original(); + + var originalAndDetach = originalAndDetachIfSuccess.bind(this, header); + + if (!sourceMap.editable()) + return originalAndDetach(); + + return /** @type {!Promise} */(sourceMap.editCompiled([range], [text]) + .then(onEditingDone.bind(this)) + .catch(onError.bind(this, header))); + + /** + * @param {?WebInspector.SourceMap.EditResult} editResult + * @return {!Promise} + * @this {WebInspector.CSSModel} + */ + function onEditingDone(editResult) + { + if (!editResult) + return originalAndDetach(); + + var edits = editResult.compiledEdits; + if (!edits.length) + return onCSSPatched.call(this, editResult, true); + + edits.sort(WebInspector.SourceEdit.comparator); + edits = edits.reverse(); + + var styleSheetIds = []; + var ranges = []; + var texts = []; + for (var edit of edits) { + styleSheetIds.push(header.id); + ranges.push(edit.oldRange); + texts.push(edit.newText); + } + return this._innerSetStyleTexts(styleSheetIds, ranges, texts, majorChange) + .then(onCSSPatched.bind(this, editResult)); + } + + /** + * @param {!WebInspector.SourceMap.EditResult} editResult + * @param {boolean} success + * @return {!Promise} + * @this {WebInspector.CSSModel} + */ + function onCSSPatched(editResult, success) + { + if (!success) + return originalAndDetach(); + + this._sourceMapByURL.set(header.sourceMapURL, editResult.map); + this.dispatchEventToListeners(WebInspector.CSSModel.Events.SourceMapChanged, { + sourceMap: editResult.map, + newSources: editResult.newSources + }); + return Promise.resolve(true); + } + + /** + * @param {!WebInspector.CSSStyleSheetHeader} header + * @param {*} error + * @return {!Promise} + * @this {WebInspector.CSSModel} + */ + function onError(header, error) + { + WebInspector.console.error(WebInspector.UIString("LiveSASS failed: %s", sourceMap.compiledURL())); + console.error(error); + this._detachSourceMap(header); + return original(); + } + + /** + * @param {!WebInspector.CSSStyleSheetHeader} header + * @return {!Promise} + * @this {WebInspector.CSSModel} + */ + function originalAndDetachIfSuccess(header) + { + return this._innerSetStyleTexts([styleSheetId], [range], [text], majorChange) + .then(detachIfSuccess.bind(this)); + + /** + * @param {boolean} success + * @return {boolean} + * @this {WebInspector.CSSModel} + */ + function detachIfSuccess(success) + { + if (success) + this._detachSourceMap(header); + return success; + } + } + }, + /** * @param {!Array} styleSheetIds * @param {!Array} ranges @@ -177,7 +362,7 @@ WebInspector.CSSModel.prototype = { * @param {boolean} majorChange * @return {!Promise} */ - setStyleTexts: function(styleSheetIds, ranges, texts, majorChange) + _innerSetStyleTexts: function(styleSheetIds, ranges, texts, majorChange) { /** * @param {?Protocol.Error} error @@ -233,7 +418,7 @@ WebInspector.CSSModel.prototype = { return false; this._domModel.markUndoableState(); var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, text, selectorPayload); - this._fireStyleSheetChanged(styleSheetId, edit); + this._fireStyleSheetChangedAndDetach(styleSheetId, edit); return true; } @@ -262,7 +447,7 @@ WebInspector.CSSModel.prototype = { return false; this._domModel.markUndoableState(); var edit = new WebInspector.CSSModel.Edit(styleSheetId, range, text, payload); - this._fireStyleSheetChanged(styleSheetId, edit); + this._fireStyleSheetChangedAndDetach(styleSheetId, edit); return true; } @@ -494,7 +679,7 @@ WebInspector.CSSModel.prototype = { return false; this._domModel.markUndoableState(); var edit = new WebInspector.CSSModel.Edit(media.parentStyleSheetId, media.range, newMediaText, mediaPayload); - this._fireStyleSheetChanged(media.parentStyleSheetId, edit); + this._fireStyleSheetChangedAndDetach(media.parentStyleSheetId, edit); return true; } @@ -528,7 +713,7 @@ WebInspector.CSSModel.prototype = { return null; this._domModel.markUndoableState(); var edit = new WebInspector.CSSModel.Edit(styleSheetId, ruleLocation, ruleText, rulePayload); - this._fireStyleSheetChanged(styleSheetId, edit); + this._fireStyleSheetChangedAndDetach(styleSheetId, edit); return new WebInspector.CSSStyleRule(this, rulePayload); } }, @@ -593,10 +778,19 @@ WebInspector.CSSModel.prototype = { */ _fireStyleSheetChanged: function(styleSheetId, edit) { + this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, edit: edit }); + }, + + /** + * @param {!CSSAgent.StyleSheetId} styleSheetId + * @param {!WebInspector.CSSModel.Edit=} edit + */ + _fireStyleSheetChangedAndDetach: function(styleSheetId, edit) + { + this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, edit: edit }); var header = this.styleSheetHeaderForId(styleSheetId); if (header) - this._detachSourceMap(header.sourceMapURL, header); - this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetChanged, { styleSheetId: styleSheetId, edit: edit }); + this._detachSourceMap(header); }, /** @@ -640,7 +834,7 @@ WebInspector.CSSModel.prototype = { if (!Object.keys(frameIdToStyleSheetIds).length) this._styleSheetIdsForURL.remove(url); } - this._detachSourceMap(header.sourceMapURL, header); + this._detachSourceMap(header); this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetRemoved, header); }, @@ -668,7 +862,7 @@ WebInspector.CSSModel.prototype = { */ setStyleSheetText: function(styleSheetId, newText, majorChange) { - var header = this._styleSheetIdToHeader.get(styleSheetId); + var header = /** @type {!WebInspector.CSSStyleSheetHeader} */(this._styleSheetIdToHeader.get(styleSheetId)); console.assert(header); newText = WebInspector.CSSModel.trimSourceURL(newText); if (header.hasSourceURL) @@ -683,12 +877,9 @@ WebInspector.CSSModel.prototype = { */ function callback(error, sourceMapURL) { - var oldSourceMapURL = header.sourceMapURL; + this._detachSourceMap(header); header.setSourceMapURL(sourceMapURL); - if (oldSourceMapURL !== header.sourceMapURL) { - this._detachSourceMap(oldSourceMapURL, header); - this._attachSourceMap(header); - } + this._attachSourceMap(header); if (error) return error; if (majorChange) @@ -734,12 +925,12 @@ WebInspector.CSSModel.prototype = { this._styleSheetIdsForURL.clear(); this._styleSheetIdToHeader.clear(); for (var i = 0; i < headers.length; ++i) { - this._detachSourceMap(headers[i].sourceMapURL, headers[i]); + this._detachSourceMap(headers[i]); this.dispatchEventToListeners(WebInspector.CSSModel.Events.StyleSheetRemoved, headers[i]); } this._sourceMapByURL.clear(); this._sourceMapURLToHeaders.clear(); - this._sourceMapLoadingPromises.clear(); + this._sourceMapLoadingStyleSheetsIds.clear(); }, /** @@ -868,7 +1059,7 @@ WebInspector.CSSDispatcher.prototype = { */ styleSheetChanged: function(styleSheetId) { - this._cssModel._fireStyleSheetChanged(styleSheetId); + this._cssModel._fireStyleSheetChangedAndDetach(styleSheetId); }, /** diff --git a/front_end/sdk/CSSStyleDeclaration.js b/front_end/sdk/CSSStyleDeclaration.js index c92a61a4b4..f2d3cc72d1 100644 --- a/front_end/sdk/CSSStyleDeclaration.js +++ b/front_end/sdk/CSSStyleDeclaration.js @@ -286,7 +286,7 @@ WebInspector.CSSStyleDeclaration.prototype = { */ setText: function(text, majorChange) { - return this._cssModel.setStyleTexts([this.styleSheetId], [this.range], [text], majorChange) + return this._cssModel.setStyleText(this.styleSheetId, this.range, text, majorChange) }, /** diff --git a/front_end/sdk/DebuggerModel.js b/front_end/sdk/DebuggerModel.js index 3c65ab385b..491729eacb 100644 --- a/front_end/sdk/DebuggerModel.js +++ b/front_end/sdk/DebuggerModel.js @@ -89,8 +89,7 @@ WebInspector.DebuggerModel.Events = { FailedToParseScriptSource: "FailedToParseScriptSource", GlobalObjectCleared: "GlobalObjectCleared", CallFrameSelected: "CallFrameSelected", - ConsoleCommandEvaluatedInSelectedCallFrame: "ConsoleCommandEvaluatedInSelectedCallFrame", - PromiseUpdated: "PromiseUpdated", + ConsoleCommandEvaluatedInSelectedCallFrame: "ConsoleCommandEvaluatedInSelectedCallFrame" } /** @enum {string} */ @@ -351,43 +350,6 @@ WebInspector.DebuggerModel.prototype = { } }, - /** - * @param {boolean} captureStacks - */ - enablePromiseTracker: function(captureStacks) - { - this._agent.enablePromiseTracker(captureStacks); - }, - - disablePromiseTracker: function() - { - this._agent.disablePromiseTracker(); - }, - - /** - * @param {number} promiseId - * @param {string=} objectGroup - * @param {function(?RuntimeAgent.RemoteObject)=} callback - */ - getPromiseById: function(promiseId, objectGroup, callback) - { - this._agent.getPromiseById(promiseId, objectGroup, innerCallback); - - /** - * @param {?Protocol.Error} error - * @param {?RuntimeAgent.RemoteObject} promise - */ - function innerCallback(error, promise) - { - if (error) { - console.error(error); - callback(null); - return; - } - callback(promise); - } - }, - flushAsyncOperationEvents: function() { this._agent.flushAsyncOperationEvents(); @@ -426,15 +388,6 @@ WebInspector.DebuggerModel.prototype = { this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.GlobalObjectCleared); }, - /** - * @param {string} eventType - * @param {!DebuggerAgent.PromiseDetails} promise - */ - _promiseUpdated: function(eventType, promise) - { - this.dispatchEventToListeners(WebInspector.DebuggerModel.Events.PromiseUpdated, { eventType: eventType, promise: promise }); - }, - /** * @param {!DebuggerAgent.AsyncOperation} operation */ @@ -1023,16 +976,6 @@ WebInspector.DebuggerDispatcher.prototype = { this._debuggerModel._breakpointResolved(breakpointId, location); }, - /** - * @override - * @param {string} eventType - * @param {!DebuggerAgent.PromiseDetails} promise - */ - promiseUpdated: function(eventType, promise) - { - this._debuggerModel._promiseUpdated(eventType, promise); - }, - /** * @override * @param {!DebuggerAgent.AsyncOperation} operation diff --git a/front_end/sdk/HAREntry.js b/front_end/sdk/HAREntry.js index ce3591f89d..ec7d526f14 100644 --- a/front_end/sdk/HAREntry.js +++ b/front_end/sdk/HAREntry.js @@ -49,13 +49,19 @@ WebInspector.HAREntry.prototype = { */ build: function() { + var ipAddress = this._request.remoteAddress(); + var portPositionInString = ipAddress.lastIndexOf(":"); + if (portPositionInString !== -1) + ipAddress = ipAddress.substr(0, portPositionInString); + var entry = { startedDateTime: WebInspector.HARLog.pseudoWallTime(this._request, this._request.startTime), time: this._request.timing ? WebInspector.HAREntry._toMilliseconds(this._request.duration) : 0, request: this._buildRequest(), response: this._buildResponse(), cache: { }, // Not supported yet. - timings: this._buildTimings() + timings: this._buildTimings(), + serverIPAddress: ipAddress }; if (this._request.connectionId !== "0") diff --git a/front_end/sdk/NetworkManager.js b/front_end/sdk/NetworkManager.js index 45986d2318..55b3b748b8 100644 --- a/front_end/sdk/NetworkManager.js +++ b/front_end/sdk/NetworkManager.js @@ -80,6 +80,8 @@ WebInspector.NetworkManager._MIMETypes = { WebInspector.NetworkManager.Conditions; /** @type {!WebInspector.NetworkManager.Conditions} */ WebInspector.NetworkManager.NoThrottlingConditions = {title: WebInspector.UIString("No throttling"), download: -1, upload: -1, latency: 0}; +/** @type {!WebInspector.NetworkManager.Conditions} */ +WebInspector.NetworkManager.OfflineConditions = {title: WebInspector.UIString("Offline"), download: 0, upload: 0, latency: 0}; WebInspector.NetworkManager.prototype = { /** diff --git a/front_end/source_frame/SourceFrame.js b/front_end/source_frame/SourceFrame.js index f59378c3b8..8b4417bceb 100644 --- a/front_end/source_frame/SourceFrame.js +++ b/front_end/source_frame/SourceFrame.js @@ -29,8 +29,9 @@ */ /** - * @extends {WebInspector.VBoxWithToolbarItems} * @constructor + * @extends {WebInspector.VBoxWithToolbarItems} + * @implements {WebInspector.Searchable} * @implements {WebInspector.Replaceable} * @param {!WebInspector.ContentProvider} contentProvider */ @@ -54,6 +55,11 @@ WebInspector.SourceFrame = function(contentProvider) this.element.addEventListener("keydown", this._handleKeyDown.bind(this), false); this._sourcePosition = new WebInspector.ToolbarText(); + + /** + * @type {?WebInspector.SearchableView} + */ + this._searchableView = null; } WebInspector.SourceFrame.Events = { @@ -230,8 +236,8 @@ WebInspector.SourceFrame.prototype = { */ onTextChanged: function(oldRange, newRange) { - if (this._searchResultsChangedCallback) - this._searchResultsChangedCallback(); + if (this._searchConfig && this._searchableView) + this.performSearch(this._searchConfig, false, false); }, /** @@ -299,13 +305,20 @@ WebInspector.SourceFrame.prototype = { onTextEditorContentLoaded: function() {}, + /** + * @param {?WebInspector.SearchableView} view + */ + setSearchableView: function(view) + { + this._searchableView = view; + }, + /** * @param {!WebInspector.SearchableView.SearchConfig} searchConfig * @param {boolean} shouldJump * @param {boolean} jumpBackwards - * @param {function(!WebInspector.Widget, number)} searchFinishedCallback */ - _doFindSearchMatches: function(searchConfig, shouldJump, jumpBackwards, searchFinishedCallback) + _doFindSearchMatches: function(searchConfig, shouldJump, jumpBackwards) { this._currentSearchResultIndex = -1; this._searchResults = []; @@ -313,7 +326,10 @@ WebInspector.SourceFrame.prototype = { var regex = searchConfig.toSearchRegex(); this._searchRegex = regex; this._searchResults = this._collectRegexMatches(regex); - searchFinishedCallback(this, this._searchResults.length); + + if (this._searchableView) + this._searchableView.updateSearchMatchesCount(this._searchResults.length); + if (!this._searchResults.length) this._textEditor.cancelSearchResultsHighlight(); else if (shouldJump && jumpBackwards) @@ -325,23 +341,22 @@ WebInspector.SourceFrame.prototype = { }, /** + * @override * @param {!WebInspector.SearchableView.SearchConfig} searchConfig * @param {boolean} shouldJump - * @param {boolean} jumpBackwards - * @param {function(!WebInspector.Widget, number)} searchFinishedCallback - * @param {function(number)} currentMatchChangedCallback - * @param {function()} searchResultsChangedCallback + * @param {boolean=} jumpBackwards */ - performSearch: function(searchConfig, shouldJump, jumpBackwards, searchFinishedCallback, currentMatchChangedCallback, searchResultsChangedCallback) + performSearch: function(searchConfig, shouldJump, jumpBackwards) { + if (this._searchableView) + this._searchableView.updateSearchMatchesCount(0); + this._resetSearch(); - this._currentSearchMatchChangedCallback = currentMatchChangedCallback; - this._searchResultsChangedCallback = searchResultsChangedCallback; - var searchFunction = this._doFindSearchMatches.bind(this, searchConfig, shouldJump, jumpBackwards, searchFinishedCallback); + this._searchConfig = searchConfig; if (this.loaded) - searchFunction.call(this); + this._doFindSearchMatches(searchConfig, shouldJump, !!jumpBackwards) else - this._delayedFindSearchMatches = searchFunction; + this._delayedFindSearchMatches = this._doFindSearchMatches.bind(this, searchConfig, shouldJump, !!jumpBackwards); this._ensureContentLoaded(); }, @@ -356,21 +371,23 @@ WebInspector.SourceFrame.prototype = { if (!this._searchResults.length) return; this._currentSearchResultIndex = -1; - if (this._currentSearchMatchChangedCallback) - this._currentSearchMatchChangedCallback(this._currentSearchResultIndex); + if (this._searchableView) + this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex); this._textEditor.highlightSearchResults(this._searchRegex, null); }, _resetSearch: function() { + delete this._searchConfig; delete this._delayedFindSearchMatches; - delete this._currentSearchMatchChangedCallback; - delete this._searchResultsChangedCallback; this._currentSearchResultIndex = -1; this._searchResults = []; delete this._searchRegex; }, + /** + * @override + */ searchCanceled: function() { var range = this._currentSearchResultIndex !== -1 ? this._searchResults[this._currentSearchResultIndex] : null; @@ -408,6 +425,9 @@ WebInspector.SourceFrame.prototype = { return this._searchResults.lowerBound(this._textEditor.selection().collapseToEnd(), WebInspector.TextRange.comparator); }, + /** + * @override + */ jumpToNextSearchResult: function() { var currentIndex = this._searchResultIndexForCurrentSelection(); @@ -415,12 +435,33 @@ WebInspector.SourceFrame.prototype = { this.jumpToSearchResult(nextIndex); }, + /** + * @override + */ jumpToPreviousSearchResult: function() { var currentIndex = this._searchResultIndexForCurrentSelection(); this.jumpToSearchResult(currentIndex - 1); }, + /** + * @override + * @return {boolean} + */ + supportsCaseSensitiveSearch: function () + { + return true; + }, + + /** + * @override + * @return {boolean} + */ + supportsRegexSearch: function() + { + return true; + }, + get currentSearchResultIndex() { return this._currentSearchResultIndex; @@ -431,8 +472,8 @@ WebInspector.SourceFrame.prototype = { if (!this.loaded || !this._searchResults.length) return; this._currentSearchResultIndex = (index + this._searchResults.length) % this._searchResults.length; - if (this._currentSearchMatchChangedCallback) - this._currentSearchMatchChangedCallback(this._currentSearchResultIndex); + if (this._searchableView) + this._searchableView.updateCurrentMatchIndex(this._currentSearchResultIndex); this._textEditor.highlightSearchResults(this._searchRegex, this._searchResults[this._currentSearchResultIndex]); }, diff --git a/front_end/source_frame/TextEditorAutocompleteController.js b/front_end/source_frame/TextEditorAutocompleteController.js index fc755380a3..3c96b052fb 100644 --- a/front_end/source_frame/TextEditorAutocompleteController.js +++ b/front_end/source_frame/TextEditorAutocompleteController.js @@ -138,7 +138,7 @@ WebInspector.TextEditorAutocompleteController.prototype = { this._prefixRange = prefixRange; if (!oldPrefixRange || prefixRange.startLine !== oldPrefixRange.startLine || prefixRange.startColumn !== oldPrefixRange.startColumn) this._updateAnchorBox(); - this._suggestBox.updateSuggestions(this._anchorBox, wordsWithPrefix, 0, true, this._textEditor.copyRange(prefixRange)); + this._suggestBox.updateSuggestions(this._anchorBox, wordsWithPrefix.map(item => ({title: item})), 0, true, this._textEditor.copyRange(prefixRange)); if (!this._suggestBox.visible()) this.finishAutocomplete(); this._onSuggestionsShownForTest(wordsWithPrefix); diff --git a/front_end/sources/CallStackSidebarPane.js b/front_end/sources/CallStackSidebarPane.js index 6f0605b1c7..d2232a31fa 100644 --- a/front_end/sources/CallStackSidebarPane.js +++ b/front_end/sources/CallStackSidebarPane.js @@ -37,6 +37,9 @@ WebInspector.CallStackSidebarPane = function() this._linkifier = new WebInspector.Linkifier(); WebInspector.moduleSetting("enableAsyncStackTraces").addChangeListener(this._asyncStackTracesStateChanged, this); WebInspector.moduleSetting("skipStackFramesPattern").addChangeListener(this._blackboxingStateChanged, this); + /** @type {!Array} */ + this.callFrames = []; + this._locationPool = new WebInspector.LiveLocationPool(); } /** @enum {string} */ @@ -55,6 +58,7 @@ WebInspector.CallStackSidebarPane.prototype = { this.callFrameList.clear(); this._linkifier.reset(); this.element.removeChildren(); + this._locationPool.disposeAll(); if (!details) { var infoElement = this.element.createChild("div", "callstack-info"); @@ -68,7 +72,6 @@ WebInspector.CallStackSidebarPane.prototype = { delete this._statusMessageElement; delete this._hiddenCallFramesMessageElement; - /** @type {!Array.} */ this.callFrames = []; this._hiddenCallFrames = 0; @@ -110,7 +113,7 @@ WebInspector.CallStackSidebarPane.prototype = { var callFrameItems = []; for (var i = 0, n = callFrames.length; i < n; ++i) { var callFrame = callFrames[i]; - var callFrameItem = new WebInspector.CallStackSidebarPane.CallFrame(callFrame.functionName, callFrame.location(), this._linkifier, callFrame); + var callFrameItem = new WebInspector.CallStackSidebarPane.CallFrame(callFrame.functionName, callFrame.location(), this._linkifier, callFrame, this._locationPool); callFrameItem.element.addEventListener("click", this._callFrameSelected.bind(this, callFrameItem), false); callFrameItems.push(callFrameItem); } @@ -131,7 +134,7 @@ WebInspector.CallStackSidebarPane.prototype = { var lineNumber = callFrame.lineNumber ? callFrame.lineNumber - 1 : 0; var columnNumber = callFrame.columnNumber ? callFrame.columnNumber - 1 : 0; var location = new WebInspector.DebuggerModel.Location(this._debuggerModel, callFrame.scriptId, lineNumber, columnNumber); - var callFrameItem = new WebInspector.CallStackSidebarPane.CallFrame(callFrame.functionName, location, this._linkifier, null, asyncCallFrameItem); + var callFrameItem = new WebInspector.CallStackSidebarPane.CallFrame(callFrame.functionName, location, this._linkifier, null, this._locationPool, asyncCallFrameItem); callFrameItem.element.addEventListener("click", this._asyncCallFrameClicked.bind(this, callFrameItem), false); callFrameItems.push(callFrameItem); } @@ -438,9 +441,10 @@ WebInspector.CallStackSidebarPane.prototype = { * @param {!WebInspector.DebuggerModel.Location} location * @param {!WebInspector.Linkifier} linkifier * @param {?WebInspector.DebuggerModel.CallFrame} debuggerCallFrame + * @param {!WebInspector.LiveLocationPool} locationPool * @param {!WebInspector.UIList.Item=} asyncCallFrame */ -WebInspector.CallStackSidebarPane.CallFrame = function(functionName, location, linkifier, debuggerCallFrame, asyncCallFrame) +WebInspector.CallStackSidebarPane.CallFrame = function(functionName, location, linkifier, debuggerCallFrame, locationPool, asyncCallFrame) { WebInspector.UIList.Item.call(this, WebInspector.beautifyFunctionName(functionName), ""); this._location = location; @@ -451,7 +455,8 @@ WebInspector.CallStackSidebarPane.CallFrame = function(functionName, location, l var locationElement = linkifier.linkifyRawLocation(location, location.script().sourceURL); this.subtitleElement.appendChild(locationElement); } else { - WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(location, this._update.bind(this)); + this._liveLocationPool = new WebInspector.LiveLocationPool(); + WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(location, this._update.bind(this), locationPool); } } diff --git a/front_end/sources/SourceMapNamesResolver.js b/front_end/sources/SourceMapNamesResolver.js index 68da339dc6..99802d14e4 100644 --- a/front_end/sources/SourceMapNamesResolver.js +++ b/front_end/sources/SourceMapNamesResolver.js @@ -38,12 +38,12 @@ WebInspector.SourceMapNamesResolver._resolveScope = function(scope) /** * @param {?string} content - * @return {!Map} + * @return {!Promise>} */ function onContent(content) { if (!content) - return new Map(); + return Promise.resolve(new Map()); var startLocation = scope.startLocation(); var endLocation = scope.endLocation(); @@ -53,56 +53,26 @@ WebInspector.SourceMapNamesResolver._resolveScope = function(scope) var scopeText = text.extract(textRange); var scopeStart = text.toSourceRange(textRange).offset; var prefix = "function fui"; - var root = acorn.parse(prefix + scopeText, {}); - /** @type {!Array} */ - var identifiers = []; - var functionDeclarationCounter = 0; - var walker = new WebInspector.ESTreeWalker(beforeVisit, afterVisit); - - /** - * @param {!ESTree.Node} node - * @return {boolean} - */ - function isFunction(node) - { - return node.type === "FunctionDeclaration" || node.type === "FunctionExpression"; - } - - /** - * @param {!ESTree.Node} node - */ - function beforeVisit(node) - { - if (isFunction(node)) - functionDeclarationCounter++; - - if (functionDeclarationCounter > 1) - return; - - if (isFunction(node) && node.params) - identifiers.pushAll(node.params); - - if (node.type === "VariableDeclarator") - identifiers.push(/** @type {!ESTree.Node} */(node.id)); - } - - /** - * @param {!ESTree.Node} node - */ - function afterVisit(node) - { - if (isFunction(node)) - functionDeclarationCounter--; - } - walker.walk(root); + return WebInspector.SourceMapNamesResolverWorker._instance().javaScriptIdentifiers(prefix + scopeText) + .then(onIdentifiers.bind(null, text, scopeStart, prefix)); + } + /** + * @param {!WebInspector.Text} text + * @param {number} scopeStart + * @param {string} prefix + * @param {!Array} identifiers + * @return {!Map} + */ + function onIdentifiers(text, scopeStart, prefix, identifiers) + { var namesMapping = new Map(); - var lineEndings = content.computeLineEndings(); + var lineEndings = text.lineEndings(); for (var i = 0; i < identifiers.length; ++i) { var id = identifiers[i]; - var start = scopeStart + id.start - prefix.length; + var start = scopeStart + id.offset - prefix.length; var lineNumber = lineEndings.lowerBound(start); var columnNumber = start - (lineNumber === 0 ? 0 : (lineEndings[lineNumber - 1] + 1)); @@ -113,10 +83,13 @@ WebInspector.SourceMapNamesResolver._resolveScope = function(scope) scope[WebInspector.SourceMapNamesResolver._cachedMapSymbol] = namesMapping; delete scope[WebInspector.SourceMapNamesResolver._cachedPromiseSymbol]; + WebInspector.SourceMapNamesResolver._scopeResolvedForTest(); return namesMapping; } } +WebInspector.SourceMapNamesResolver._scopeResolvedForTest = function() { } + /** * @param {!WebInspector.DebuggerModel.CallFrame} callFrame * @return {!Promise.>} @@ -206,53 +179,19 @@ WebInspector.SourceMapNamesResolver._resolveExpression = function(callFrame, uiS /** * @param {?string} content - * @return {string} + * @return {!Promise} */ function onContent(content) { if (!content) - return ""; + return Promise.resolve(""); var text = new WebInspector.Text(content); var textRange = sourceMap.reverseMapTextRange(uiSourceCode.url(), new WebInspector.TextRange(lineNumber, startColumnNumber, lineNumber, endColumnNumber)); var originalText = text.extract(textRange); if (!originalText) - return ""; - - var tokenizer = acorn.tokenizer(originalText, {ecmaVersion: 6}); - try { - var token = tokenizer.getToken(); - while (token.type !== acorn.tokTypes.eof && WebInspector.AcornTokenizer.punctuator(token)) - token = tokenizer.getToken(); - - var startIndex = token.start; - var endIndex = token.end; - var openBracketsCounter = 0; - while (token.type !== acorn.tokTypes.eof) { - var isIdentifier = WebInspector.AcornTokenizer.identifier(token); - var isThis = WebInspector.AcornTokenizer.keyword(token, "this"); - var isString = token.type === acorn.tokTypes.string; - if (!isThis && !isIdentifier && !isString) - break; - - endIndex = token.end; - token = tokenizer.getToken(); - while (WebInspector.AcornTokenizer.punctuator(token, ".[]")) { - if (WebInspector.AcornTokenizer.punctuator(token, "[")) - openBracketsCounter++; - - if (WebInspector.AcornTokenizer.punctuator(token, "]")) { - endIndex = openBracketsCounter > 0 ? token.end : endIndex; - openBracketsCounter--; - } - - token = tokenizer.getToken(); - } - } - return originalText.substring(startIndex, endIndex); - } catch (e) { - return ""; - } + return Promise.resolve(""); + return WebInspector.SourceMapNamesResolverWorker._instance().evaluatableJavaScriptSubstring(originalText); } } @@ -519,3 +458,99 @@ WebInspector.SourceMapNamesResolver.RemoteObject.prototype = { __proto__: WebInspector.RemoteObject.prototype } + +/** + * @constructor + */ +WebInspector.SourceMapNamesResolverWorker = function() +{ + this._worker = new WorkerRuntime.Worker("formatter_worker"); + this._worker.onmessage = this._onMessage.bind(this); + this._methodNames = []; + this._contents = []; + this._callbacks = []; +} + +WebInspector.SourceMapNamesResolverWorker.prototype = { + /** + * @param {string} text + * @return {!Promise} + */ + evaluatableJavaScriptSubstring: function(text) + { + var callback; + var promise = new Promise(fulfill => callback = fulfill); + this._methodNames.push("evaluatableJavaScriptSubstring"); + this._contents.push(text); + this._callbacks.push(this._evaluatableJavaScriptSubstringCallback.bind(this, callback)); + this._maybeRunTask(); + return promise; + }, + + /** + * @param {function(string)} callback + * @param {!MessageEvent} event + */ + _evaluatableJavaScriptSubstringCallback: function(callback, event) + { + callback.call(null, /** @type {string} */(event.data)); + }, + + /** + * @param {string} text + * @return {!Promise>} + */ + javaScriptIdentifiers: function(text) + { + var callback; + var promise = new Promise(fulfill => callback = fulfill); + this._methodNames.push("javaScriptIdentifiers"); + this._contents.push(text); + this._callbacks.push(this._javaScriptIdentifiersCallback.bind(this, callback)); + this._maybeRunTask(); + return promise; + }, + + /** + * @param {function(!Array)} callback + * @param {!MessageEvent} event + */ + _javaScriptIdentifiersCallback: function(callback, event) + { + callback.call(null, /** @type {!Array} */(event.data)); + }, + + /** + * @param {!MessageEvent} event + */ + _onMessage: function(event) + { + var callback = this._callbacks[0]; + this._methodNames.shift(); + this._contents.shift(); + this._callbacks.shift(); + callback.call(null, event); + this._runningTask = false; + this._maybeRunTask(); + }, + + _maybeRunTask: function() + { + if (this._runningTask || !this._methodNames.length) + return; + this._runningTask = true; + var methodName = this._methodNames[0]; + var content = this._contents[0]; + this._worker.postMessage({ method: methodName, params: { content: content } }); + } +} + +/** + * @return {!WebInspector.SourceMapNamesResolverWorker} + */ +WebInspector.SourceMapNamesResolverWorker._instance = function() +{ + if (!WebInspector.SourceMapNamesResolverWorker._instanceObject) + WebInspector.SourceMapNamesResolverWorker._instanceObject = new WebInspector.SourceMapNamesResolverWorker(); + return WebInspector.SourceMapNamesResolverWorker._instanceObject; +} \ No newline at end of file diff --git a/front_end/sources/SourcesPanel.js b/front_end/sources/SourcesPanel.js index ff6ae63c74..b24be6139e 100644 --- a/front_end/sources/SourcesPanel.js +++ b/front_end/sources/SourcesPanel.js @@ -103,6 +103,9 @@ WebInspector.SourcesPanel = function(workspaceForTest) this._updateDebuggerButtons(); this._pauseOnExceptionEnabledChanged(); WebInspector.moduleSetting("pauseOnExceptionEnabled").addChangeListener(this._pauseOnExceptionEnabledChanged, this); + + this._liveLocationPool = new WebInspector.LiveLocationPool(); + this._setTarget(WebInspector.context.flavor(WebInspector.Target)); WebInspector.breakpointManager.addEventListener(WebInspector.BreakpointManager.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this); WebInspector.context.addFlavorChangeListener(WebInspector.Target, this._onCurrentTargetChanged, this); @@ -291,7 +294,7 @@ WebInspector.SourcesPanel.prototype = { } } else { if (details.callFrames.length) - WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(details.callFrames[0].location(), didGetUILocation.bind(this)); + WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(details.callFrames[0].location(), didGetUILocation.bind(this), this._liveLocationPool); else console.warn("ScriptsPanel paused, but callFrames.length is zero."); // TODO remove this once we understand this case better } @@ -436,7 +439,7 @@ WebInspector.SourcesPanel.prototype = { this.sidebarPanes.scopechain.update(callFrame); this.sidebarPanes.watchExpressions.refreshExpressions(); this.sidebarPanes.callstack.setSelectedCallFrame(callFrame); - WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(callFrame.location(), this._executionLineChanged.bind(this)); + WebInspector.debuggerWorkspaceBinding.createCallFrameLiveLocation(callFrame.location(), this._executionLineChanged.bind(this), this._liveLocationPool); }, /** @@ -507,6 +510,7 @@ WebInspector.SourcesPanel.prototype = { if (this._switchToPausedTargetTimeout) clearTimeout(this._switchToPausedTargetTimeout); + this._liveLocationPool.disposeAll(); }, /** diff --git a/front_end/sources/SourcesView.js b/front_end/sources/SourcesView.js index 337ba81e46..e96704fe77 100644 --- a/front_end/sources/SourcesView.js +++ b/front_end/sources/SourcesView.js @@ -316,9 +316,18 @@ WebInspector.SourcesView.prototype = { if (this._currentUISourceCode === uiSourceCode) return sourceView; + var currentFrame = this.currentSourceFrame(); + if (currentFrame) + currentFrame.setSearchableView(null); + this._currentUISourceCode = uiSourceCode; this._editorContainer.showFile(uiSourceCode); this._updateScriptViewToolbarItems(); + + currentFrame = this.currentSourceFrame(); + if (currentFrame) + currentFrame.setSearchableView(this._searchableView); + return sourceView; }, @@ -497,8 +506,6 @@ WebInspector.SourcesView.prototype = { */ performSearch: function(searchConfig, shouldJump, jumpBackwards) { - this._searchableView.updateSearchMatchesCount(0); - var sourceFrame = this.currentSourceFrame(); if (!sourceFrame) return; @@ -506,37 +513,7 @@ WebInspector.SourcesView.prototype = { this._searchView = sourceFrame; this._searchConfig = searchConfig; - /** - * @param {!WebInspector.Widget} view - * @param {number} searchMatches - * @this {WebInspector.SourcesView} - */ - function finishedCallback(view, searchMatches) - { - if (!searchMatches) - return; - - this._searchableView.updateSearchMatchesCount(searchMatches); - } - - /** - * @param {number} currentMatchIndex - * @this {WebInspector.SourcesView} - */ - function currentMatchChanged(currentMatchIndex) - { - this._searchableView.updateCurrentMatchIndex(currentMatchIndex); - } - - /** - * @this {WebInspector.SourcesView} - */ - function searchResultsChanged() - { - this.performSearch(this._searchConfig, false, false); - } - - this._searchView.performSearch(this._searchConfig, shouldJump, !!jumpBackwards, finishedCallback.bind(this), currentMatchChanged.bind(this), searchResultsChanged.bind(this)); + this._searchView.performSearch(this._searchConfig, shouldJump, jumpBackwards); }, /** diff --git a/front_end/sources/module.json b/front_end/sources/module.json index 8903b9bde2..fd07496e71 100644 --- a/front_end/sources/module.json +++ b/front_end/sources/module.json @@ -343,7 +343,6 @@ } ], "dependencies": [ - "es_tree", "components", "source_frame", "snippets", diff --git a/front_end/timeline/TimelineFlameChart.js b/front_end/timeline/TimelineFlameChart.js index aa805541f9..e9e4e1b2b7 100644 --- a/front_end/timeline/TimelineFlameChart.js +++ b/front_end/timeline/TimelineFlameChart.js @@ -291,7 +291,7 @@ WebInspector.TimelineFlameChartDataProvider = function(model, frameModel, irMode color: WebInspector.themeSupport.patchColor("#222", WebInspector.ThemeSupport.ColorUsage.Foreground), font: this._font, backgroundColor: WebInspector.themeSupport.patchColor("white", WebInspector.ThemeSupport.ColorUsage.Background), - nestingLevel: 0, + nestingLevel: 0 }; this._headerLevel2 = { @@ -388,17 +388,16 @@ WebInspector.TimelineFlameChartDataProvider.prototype = { this._appendInteractionRecords(); var threads = this._model.virtualThreads(); + this._appendThreadTimelineData(WebInspector.UIString("Main"), this._model.mainThreadEvents(), this._model.mainThreadAsyncEvents(), true); var compositorThreads = threads.filter(thread => thread.name.startsWith("CompositorTileWorker")); var otherThreads = threads.filter(thread => !thread.name.startsWith("CompositorTileWorker")); if (compositorThreads.length) { - this._appendHeader(WebInspector.UIString("Rasterizer Threads"), this._headerLevel1); + this._appendHeader(WebInspector.UIString("Raster"), this._headerLevel1); for (var i = 0; i < compositorThreads.length; ++i) this._appendSyncEvents(compositorThreads[i].events, WebInspector.UIString("Rasterizer Thread %d", i), this._headerLevel2); } this._appendGPUEvents(); - this._appendThreadTimelineData(WebInspector.UIString("Main Thread"), this._model.mainThreadEvents(), this._model.mainThreadAsyncEvents(), true); - otherThreads.forEach(thread => this._appendThreadTimelineData(thread.name, thread.events, thread.asyncEventsByGroup)); /** @@ -654,7 +653,7 @@ WebInspector.TimelineFlameChartDataProvider.prototype = { if (type === WebInspector.TimelineFlameChartEntryType.Frame) return "white"; if (type === WebInspector.TimelineFlameChartEntryType.InteractionRecord) - return WebInspector.TimelineUIUtils.interactionPhaseColor(/** @type {!WebInspector.TimelineIRModel.Phases} */ (this._entryData[entryIndex])); + return "transparent"; return ""; }, @@ -681,10 +680,8 @@ WebInspector.TimelineFlameChartDataProvider.prototype = { barWidth -= 2 * hPadding; barY += vPadding; barHeight -= 2 * vPadding + 1; - context.fillStyle = frame.idle ? "white" : "#eee"; + context.fillStyle = frame.idle ? "white" : (frame.hasWarnings() ? "#fad1d1" : "#d7f0d1"); context.fillRect(barX, barY, barWidth, barHeight); - if (frame.hasWarnings()) - paintWarningDecoration(barX, barWidth); var frameDurationText = Number.preciseMillisToString(frame.duration, 1); var textWidth = context.measureText(frameDurationText).width; if (barWidth >= textWidth) { @@ -694,6 +691,15 @@ WebInspector.TimelineFlameChartDataProvider.prototype = { return true; } + if (type === WebInspector.TimelineFlameChartEntryType.InteractionRecord) { + var color = WebInspector.TimelineUIUtils.interactionPhaseColor(/** @type {!WebInspector.TimelineIRModel.Phases} */ (this._entryData[entryIndex])); + context.fillStyle = color; + context.fillRect(barX, barY, barWidth - 1, 2); + context.fillRect(barX, barY - 3, 2, 3); + context.fillRect(barX + barWidth - 3, barY - 3, 2, 3); + return false; + } + if (type === WebInspector.TimelineFlameChartEntryType.Event) { var event = /** @type {!WebInspector.TracingModel.Event} */ (this._entryData[entryIndex]); if (event && event.warning) diff --git a/front_end/timeline/TimelineModel.js b/front_end/timeline/TimelineModel.js index 243d057d79..70686f5f9b 100644 --- a/front_end/timeline/TimelineModel.js +++ b/front_end/timeline/TimelineModel.js @@ -117,6 +117,7 @@ WebInspector.TimelineModel.RecordType = { V8Sample: "V8Sample", JitCodeAdded: "JitCodeAdded", JitCodeMoved: "JitCodeMoved", + ParseScriptOnBackground: "v8.parseOnBackground", UpdateCounters: "UpdateCounters", diff --git a/front_end/timeline/TimelinePanel.js b/front_end/timeline/TimelinePanel.js index a048529b60..fcd5f5b543 100644 --- a/front_end/timeline/TimelinePanel.js +++ b/front_end/timeline/TimelinePanel.js @@ -119,6 +119,7 @@ WebInspector.TimelinePanel = function() this._detailsSplitWidget.hideSidebar(); WebInspector.targetManager.addEventListener(WebInspector.TargetManager.Events.SuspendStateChanged, this._onSuspendStateChanged, this); this._showRecordingHelpMessage(); + this._locationPool = new WebInspector.LiveLocationPool(); } /** @@ -1280,7 +1281,7 @@ WebInspector.TimelinePanel.prototype = { var time = lineInfo[1]; var rawLocation = debuggerModel.createRawLocationByURL(url, line, 0); if (rawLocation) - new WebInspector.TimelineUIUtils.LineLevelProfilePresentation(rawLocation, time); + new WebInspector.TimelineUIUtils.LineLevelProfilePresentation(rawLocation, time, this._locationPool); else if (uiSourceCode) uiSourceCode.addLineDecoration(line, WebInspector.TimelineUIUtils.PerformanceLineDecorator.type, time); } @@ -1289,6 +1290,7 @@ WebInspector.TimelinePanel.prototype = { _resetLineLevelCPUProfile: function() { + this._locationPool.disposeAll(); WebInspector.workspace.uiSourceCodes().forEach(uiSourceCode => uiSourceCode.removeAllLineDecorations(WebInspector.TimelineUIUtils.PerformanceLineDecorator.type)); }, diff --git a/front_end/timeline/TimelineUIUtils.js b/front_end/timeline/TimelineUIUtils.js index 9e77d90fc3..b0cfb04149 100644 --- a/front_end/timeline/TimelineUIUtils.js +++ b/front_end/timeline/TimelineUIUtils.js @@ -90,6 +90,7 @@ WebInspector.TimelineUIUtils._initEventStyles = function() eventStyles[recordTypes.XHRLoad] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("XHR Load"), categories["scripting"]); eventStyles[recordTypes.CompileScript] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Compile Script"), categories["scripting"]); eventStyles[recordTypes.EvaluateScript] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Evaluate Script"), categories["scripting"]); + eventStyles[recordTypes.ParseScriptOnBackground] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Parse Script"), categories["scripting"]); eventStyles[recordTypes.MarkLoad] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("Load event"), categories["scripting"], true); eventStyles[recordTypes.MarkDOMContent] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("DOMContentLoaded event"), categories["scripting"], true); eventStyles[recordTypes.MarkFirstPaint] = new WebInspector.TimelineRecordStyle(WebInspector.UIString("First paint"), categories["painting"], true); @@ -391,13 +392,14 @@ WebInspector.TimelineUIUtils.buildDetailsTextForTraceEvent = function(event, tar case recordType.EvaluateScript: var url = eventData["url"]; if (url) - detailsText = detailsText = WebInspector.displayNameForURL(url) + ":" + eventData["lineNumber"]; + detailsText = WebInspector.displayNameForURL(url) + ":" + eventData["lineNumber"]; break; + case recordType.ParseScriptOnBackground: case recordType.XHRReadyStateChange: case recordType.XHRLoad: var url = eventData["url"]; if (url) - detailsText = detailsText = WebInspector.displayNameForURL(url); + detailsText = WebInspector.displayNameForURL(url); break; case recordType.WebSocketCreate: @@ -540,6 +542,11 @@ WebInspector.TimelineUIUtils.buildDetailsNodeForTraceEvent = function(event, tar if (url) details = linkifyLocation("", url, eventData["lineNumber"], 0); break; + case recordType.ParseScriptOnBackground: + var url = eventData["url"]; + if (url) + details = linkifyLocation("", url, 0, 0); + break; default: if (event.hasCategory(WebInspector.TimelineModel.Category.Console)) detailsText = null; @@ -2114,11 +2121,12 @@ WebInspector.TimelineUIUtils.eventWarning = function(event, warningType) * @constructor * @param {!WebInspector.DebuggerModel.Location} rawLocation * @param {number} time + * @param {!WebInspector.LiveLocationPool} locationPool */ -WebInspector.TimelineUIUtils.LineLevelProfilePresentation = function(rawLocation, time) +WebInspector.TimelineUIUtils.LineLevelProfilePresentation = function(rawLocation, time, locationPool) { this._time = time; - WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this.updateLocation.bind(this)); + WebInspector.debuggerWorkspaceBinding.createLiveLocation(rawLocation, this.updateLocation.bind(this), locationPool); } WebInspector.TimelineUIUtils.LineLevelProfilePresentation.prototype = { diff --git a/front_end/ui/FilterBar.js b/front_end/ui/FilterBar.js index 281deb6fa9..3ff751863e 100644 --- a/front_end/ui/FilterBar.js +++ b/front_end/ui/FilterBar.js @@ -264,6 +264,14 @@ WebInspector.TextFilterUI.prototype = { return this._filterElement; }, + /** + * @return {boolean} + */ + isRegexChecked: function() + { + return this._supportRegex ? this._regexCheckBox.checked : false; + }, + /** * @return {string} */ @@ -341,6 +349,11 @@ WebInspector.TextFilterUI.prototype = { { if (!this._suggestionBuilder) return; + if (this.isRegexChecked()) { + if (this._suggestBox.visible()) + this._suggestBox.hide(); + return; + } var suggestions = this._suggestionBuilder.buildSuggestions(this._filterInputElement); if (suggestions && suggestions.length) { if (this._suppressSuggestion) @@ -348,7 +361,7 @@ WebInspector.TextFilterUI.prototype = { else this._suggestionBuilder.applySuggestion(this._filterInputElement, suggestions[0], true); var anchorBox = this._filterInputElement.boxInWindow().relativeTo(new AnchorBox(-3, 0)); - this._suggestBox.updateSuggestions(anchorBox, suggestions, 0, true, ""); + this._suggestBox.updateSuggestions(anchorBox, suggestions.map(item => ({title: item})), 0, true, ""); } else { this._suggestBox.hide(); } @@ -369,7 +382,7 @@ WebInspector.TextFilterUI.prototype = { this._regex = null; this._filterInputElement.classList.remove("filter-text-invalid"); if (filterQuery) { - if (this._supportRegex && this._regexCheckBox.checked) { + if (this.isRegexChecked()) { try { this._regex = new RegExp(filterQuery, "i"); } catch (e) { diff --git a/front_end/ui/SuggestBox.js b/front_end/ui/SuggestBox.js index f1cb273a04..188772dbbc 100644 --- a/front_end/ui/SuggestBox.js +++ b/front_end/ui/SuggestBox.js @@ -71,6 +71,11 @@ WebInspector.SuggestBox = function(suggestBoxDelegate, maxItemsHeight) this._asyncDetailsPromises = new Map(); } +/** + * @typedef Array.<{title: string, className: (string|undefined)}> + */ +WebInspector.SuggestBox.Suggestions; + WebInspector.SuggestBox.prototype = { /** * @return {boolean} @@ -240,11 +245,12 @@ WebInspector.SuggestBox.prototype = { /** * @param {string} prefix * @param {string} text + * @param {string|undefined} className * @param {number} index */ - _createItemElement: function(prefix, text, index) + _createItemElement: function(prefix, text, className, index) { - var element = createElementWithClass("div", "suggest-box-content-item source-code"); + var element = createElementWithClass("div", "suggest-box-content-item source-code " + (className || "")); element.tabIndex = -1; if (prefix && prefix.length && !text.indexOf(prefix)) { element.createChild("span", "prefix").textContent = prefix; @@ -259,7 +265,7 @@ WebInspector.SuggestBox.prototype = { }, /** - * @param {!Array.} items + * @param {!WebInspector.SuggestBox.Suggestions} items * @param {string} userEnteredText * @param {function(number): !Promise<{detail:string, description:string}>=} asyncDetails */ @@ -273,7 +279,7 @@ WebInspector.SuggestBox.prototype = { for (var i = 0; i < items.length; ++i) { var item = items[i]; - var currentItemElement = this._createItemElement(userEnteredText, item, i); + var currentItemElement = this._createItemElement(userEnteredText, item.title, item.className, i); this._element.appendChild(currentItemElement); } }, @@ -338,7 +344,7 @@ WebInspector.SuggestBox.prototype = { }, /** - * @param {!Array.} completions + * @param {!WebInspector.SuggestBox.Suggestions} completions * @param {boolean} canShowForSingleItem * @param {string} userEnteredText */ @@ -351,7 +357,7 @@ WebInspector.SuggestBox.prototype = { return true; // Do not show a single suggestion if it is the same as user-entered prefix, even if allowed to show single-item suggest boxes. - return canShowForSingleItem && completions[0] !== userEnteredText; + return canShowForSingleItem && completions[0].title !== userEnteredText; }, _ensureRowCountPerViewport: function() @@ -366,7 +372,7 @@ WebInspector.SuggestBox.prototype = { /** * @param {!AnchorBox} anchorBox - * @param {!Array.} completions + * @param {!WebInspector.SuggestBox.Suggestions} completions * @param {number} selectedIndex * @param {boolean} canShowForSingleItem * @param {string} userEnteredText diff --git a/front_end/ui/TabbedPane.js b/front_end/ui/TabbedPane.js index 1a342f310d..75b60c75e1 100644 --- a/front_end/ui/TabbedPane.js +++ b/front_end/ui/TabbedPane.js @@ -83,20 +83,29 @@ WebInspector.TabbedPane.prototype = { return this._currentTab ? this._currentTab.view : null; }, + /** + * @return {!Array.} + */ + tabIds: function() + { + return this._tabs.map(tab => tab._id); + }, + /** * @return {!Array.} */ tabViews: function() { - /** - * @param {!WebInspector.TabbedPaneTab} tab - * @return {!WebInspector.Widget} - */ - function tabToView(tab) - { - return tab.view; - } - return this._tabs.map(tabToView); + return this._tabs.map(tab => tab.view); + }, + + /** + * @param {string} tabId + * @return {?WebInspector.Widget} + */ + tabView: function(tabId) + { + return this._tabsById[tabId] ? this._tabsById[tabId].view : null; }, /** @@ -361,6 +370,16 @@ WebInspector.TabbedPane.prototype = { this._updateTabElements(); }, + /** + * @param {string} id + * @param {boolean} enabled + */ + setTabEnabled: function(id, enabled) + { + var tab = this._tabsById[id]; + tab.tabElement.classList.toggle("disabled", !enabled); + }, + /** * @param {string} id * @param {string} className diff --git a/front_end/ui/TextPrompt.js b/front_end/ui/TextPrompt.js index 84e2a53008..79952d3939 100644 --- a/front_end/ui/TextPrompt.js +++ b/front_end/ui/TextPrompt.js @@ -471,7 +471,7 @@ WebInspector.TextPrompt.prototype = { /** * @param {string} prefix - * @return {!Array.} + * @return {!WebInspector.SuggestBox.Suggestions} */ additionalCompletions: function(prefix) { @@ -489,18 +489,20 @@ WebInspector.TextPrompt.prototype = { _completionsReady: function(selection, originalWordPrefixRange, reverse, force, completions, selectedIndex) { var prefix = originalWordPrefixRange.toString(); - if (prefix || force) { - if (prefix) - completions = completions.concat(this.additionalCompletions(prefix)); - else - completions = this.additionalCompletions(prefix).concat(completions); - } // Filter out dupes. var store = new Set(); completions = completions.filter(item => !store.has(item) && !!store.add(item)); + var annotatedCompletions = completions.map(item => ({title: item})); + + if (prefix || force) { + if (prefix) + annotatedCompletions = annotatedCompletions.concat(this.additionalCompletions(prefix)); + else + annotatedCompletions = this.additionalCompletions(prefix).concat(annotatedCompletions); + } - if (!this._waitingForCompletions || !completions.length) { + if (!this._waitingForCompletions || !annotatedCompletions.length) { this.hideSuggestBox(); return; } @@ -522,7 +524,7 @@ WebInspector.TextPrompt.prototype = { this._userEnteredText = fullWordRange.toString(); if (this._suggestBox) - this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selection, fullWordRange), completions, selectedIndex, !this.isCaretAtEndOfPrompt(), this._userEnteredText); + this._suggestBox.updateSuggestions(this._boxForAnchorAtStart(selection, fullWordRange), annotatedCompletions, selectedIndex, !this.isCaretAtEndOfPrompt(), this._userEnteredText); if (selectedIndex === -1) return; @@ -531,7 +533,7 @@ WebInspector.TextPrompt.prototype = { this._commonPrefix = this._buildCommonPrefix(completions, wordPrefixLength); if (this.isCaretAtEndOfPrompt()) { - var completionText = completions[selectedIndex]; + var completionText = annotatedCompletions[selectedIndex].title; var prefixText = this._userEnteredRange.toString(); var suffixText = completionText.substring(wordPrefixLength); this._userEnteredRange.deleteContents(); @@ -827,7 +829,7 @@ WebInspector.TextPromptWithHistory.prototype = { /** * @override * @param {string} prefix - * @return {!Array.} + * @return {!WebInspector.SuggestBox.Suggestions} */ additionalCompletions: function(prefix) { @@ -835,11 +837,15 @@ WebInspector.TextPromptWithHistory.prototype = { return []; var result = []; var text = this.text(); + var set = new Set(); for (var i = this._data.length - 1; i >= 0 && result.length < 50; --i) { var item = this._data[i]; if (!item.startsWith(text)) continue; - result.push(item.substring(text.length - prefix.length)); + if (set.has(item)) + continue; + set.add(item); + result.push({title: item.substring(text.length - prefix.length), className: "additional"}); } return result; }, diff --git a/front_end/ui/Toolbar.js b/front_end/ui/Toolbar.js index 70c374e8dc..c57e0cf7b4 100644 --- a/front_end/ui/Toolbar.js +++ b/front_end/ui/Toolbar.js @@ -101,6 +101,11 @@ WebInspector.Toolbar.prototype = { this.appendToolbarItem(new WebInspector.ToolbarSeparator()); }, + appendSpacer: function() + { + this.appendToolbarItem(new WebInspector.ToolbarSeparator(true)); + }, + /** * @param {string} text */ @@ -719,10 +724,11 @@ WebInspector.ToolbarSettingToggle.prototype = { /** * @constructor * @extends {WebInspector.ToolbarItem} + * @param {boolean=} spacer */ -WebInspector.ToolbarSeparator = function() +WebInspector.ToolbarSeparator = function(spacer) { - WebInspector.ToolbarItem.call(this, createElementWithClass("div", "toolbar-divider")); + WebInspector.ToolbarItem.call(this, createElementWithClass("div", spacer ? "toolbar-spacer" : "toolbar-divider")); } WebInspector.ToolbarSeparator.prototype = { diff --git a/front_end/ui/suggestBox.css b/front_end/ui/suggestBox.css index 4752503cb6..e33ff52c40 100644 --- a/front_end/ui/suggestBox.css +++ b/front_end/ui/suggestBox.css @@ -91,6 +91,30 @@ white-space: nowrap; } +.suggest-box .suggest-box-content-item.additional { + background-color: #f9f9f9; +} + +.suggest-box .suggest-box-content-item.additional::before { + display: inline-block; + content: ""; + -webkit-user-select: none; + background-image: url(Images/toolbarButtonGlyphs.png); + background-size: 352px 168px; + width: 10px; + height: 10px; + position: relative; + top: 2px; + margin-right: 4px; + background-position: -192px -96px; +} + +@media (-webkit-min-device-pixel-ratio: 1.5) { +.suggest-box .suggest-box-content-item.additional::before { + background-image: url(Images/toolbarButtonGlyphs_2x.png); +} +} /* media */ + .suggest-box .suggest-box-content-item .prefix { font-weight: bold; } diff --git a/front_end/ui/tabbedPane.css b/front_end/ui/tabbedPane.css index f2f5b88c3e..2223c6d0d0 100644 --- a/front_end/ui/tabbedPane.css +++ b/front_end/ui/tabbedPane.css @@ -260,6 +260,11 @@ visibility: visible; } +.tabbed-pane-header-tab.disabled { + opacity: 0.5; + pointer-events: none; +} + .tabbed-pane-header.tabbed-pane-no-header-background { background-color: transparent; } diff --git a/front_end/ui/toolbar.css b/front_end/ui/toolbar.css index 45d3c61b75..6b93471648 100644 --- a/front_end/ui/toolbar.css +++ b/front_end/ui/toolbar.css @@ -227,6 +227,10 @@ input.toolbar-item.hover { height: 16px; } +.toolbar-spacer { + flex: auto; +} + /* Long click */ .long-click-glyph { @@ -336,6 +340,10 @@ input.toolbar-item.hover { transform: scaleX(-1); } +.stop-toolbar-item.toolbar-glyph { + -webkit-mask-position: -96px -48px; +} + .pause-on-exceptions-toolbar-item.toolbar-glyph { -webkit-mask-position: -256px 0; } @@ -356,6 +364,10 @@ input.toolbar-item.hover { -webkit-mask-position: 0 -24px; } +.notification-toolbar-item.toolbar-glyph { + -webkit-mask-position: -32px -120px; +} + .format-toolbar-item.toolbar-glyph { -webkit-mask-position: -256px -24px; } diff --git a/front_end/ui_lazy/FlameChart.js b/front_end/ui_lazy/FlameChart.js index da1c28a86d..5cdacf611e 100644 --- a/front_end/ui_lazy/FlameChart.js +++ b/front_end/ui_lazy/FlameChart.js @@ -1354,26 +1354,12 @@ WebInspector.FlameChart.prototype = { if (groups.length && lastGroupOffset < top + height) context.fillRect(0, lastGroupOffset + 2, width, top + height - lastGroupOffset) - context.strokeStyle = WebInspector.themeSupport.patchColor("#ddd", colorUsage.Background); - context.beginPath(); - forEachGroup((offset, index, group, isFirst) => { - if (isFirst) - return; - if (group.style.padding > 1) - hLine(offset - 1.5); - if (group.style.padding > 2) - hLine(offset - group.style.padding + 1.5); - }); - hLine(lastGroupOffset + 1.5); - context.stroke(); - context.strokeStyle = WebInspector.themeSupport.patchColor("#bbb", colorUsage.Background); context.beginPath(); forEachGroup((offset, index, group, isFirst) => { if (isFirst || group.style.padding < 4) return; - hLine(offset - group.style.padding + 0.5); - hLine(offset - 0.5); + hLine(offset - 2.5); }); hLine(lastGroupOffset + 0.5); context.stroke(); @@ -1422,10 +1408,6 @@ WebInspector.FlameChart.prototype = { context.strokeStyle = WebInspector.themeSupport.patchColor("#ddd", colorUsage.Background); context.beginPath(); - forEachGroup((offset, index, group) => { - if (group.expanded) - hLine(offset + barHeight - 0.5); - }); context.stroke(); context.restore(); diff --git a/protocol.json b/protocol.json index 9462985bd0..3b20f37a90 100644 --- a/protocol.json +++ b/protocol.json @@ -455,9 +455,6 @@ }, { "name": "requestAppBanner", - "returns": [ - { "name": "result", "type": "boolean" } - ], "hidden": true, "handlers": ["browser"] } @@ -3454,21 +3451,6 @@ "description": "Error data for setScriptSource command. Contains uncompilable script source error.", "hidden": true }, - { - "id": "PromiseDetails", - "type": "object", - "description": "Information about the promise. All fields but id are optional and if present they reflect the new state of the property on the promise with given id.", - "properties": [ - { "name": "id", "type": "integer", "description": "Unique id of the promise." }, - { "name": "status", "type": "string", "optional": true, "enum": ["pending", "resolved", "rejected"], "description": "Status of the promise." }, - { "name": "parentId", "type": "integer", "optional": true, "description": "Id of the parent promise." }, - { "name": "creationTime", "type": "number", "optional": true, "description": "Creation time of the promise." }, - { "name": "settlementTime", "type": "number", "optional": true, "description": "Settlement time of the promise." }, - { "name": "creationStack", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript stack trace on promise creation." }, - { "name": "settlementStack", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript stack trace on promise settlement." } - ], - "hidden": true - }, { "id": "AsyncOperation", "type": "object", @@ -3724,31 +3706,6 @@ "hidden": true, "description": "Enables or disables async call stacks tracking." }, - { - "name": "enablePromiseTracker", - "parameters": [ - { "name": "captureStacks", "type": "boolean", "optional": true, "description": "Whether to capture stack traces for promise creation and settlement events (default: false)." } - ], - "hidden": true, - "description": "Enables promise tracking, information about Promises created or updated will now be stored on the backend." - }, - { - "name": "disablePromiseTracker", - "hidden": true, - "description": "Disables promise tracking." - }, - { - "name": "getPromiseById", - "parameters": [ - { "name": "promiseId", "type": "integer" }, - { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } - ], - "returns": [ - { "name": "promise", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for Promise with specified ID, if any." } - ], - "hidden": true, - "description": "Returns Promise with specified ID." - }, { "name": "flushAsyncOperationEvents", "hidden": true, @@ -3843,15 +3800,6 @@ "name": "resumed", "description": "Fired when the virtual machine resumed execution." }, - { - "name": "promiseUpdated", - "parameters": [ - { "name": "eventType", "type": "string", "enum": ["new", "update", "gc"], "description": "Type of the event." }, - { "name": "promise", "$ref": "PromiseDetails", "description": "Information about the updated Promise." } - ], - "description": "Fired when a Promise is created, updated or garbage collected.", - "hidden": true - }, { "name": "asyncOperationStarted", "parameters": [ diff --git a/scripts/compile_frontend.py b/scripts/compile_frontend.py index fa792f08ac..b95dee3f18 100755 --- a/scripts/compile_frontend.py +++ b/scripts/compile_frontend.py @@ -78,6 +78,8 @@ def to_platform_path_exact(filepath): protocol_externs_file = path.join(devtools_frontend_path, 'protocol_externs.js') injected_script_source_name = path.join(v8_inspector_path, 'InjectedScriptSource.js') injected_script_externs_file = path.join(v8_inspector_path, 'injected_script_externs.js') +debugger_script_source_name = path.join(v8_inspector_path, 'DebuggerScript.js') +debugger_script_externs_file = path.join(v8_inspector_path, 'debugger_script_externs.js') jsmodule_name_prefix = 'jsmodule_' runtime_module_name = '_runtime' @@ -403,6 +405,16 @@ def replace_function(matchobj): injectedScriptCompileProc = popen(command) +print 'Compiling DebuggerScript.js...' + +command = spawned_compiler_command + [ + '--externs', to_platform_path_exact(debugger_script_externs_file), + '--module', jsmodule_name_prefix + 'debugger_script' + ':1', + '--js', to_platform_path(debugger_script_source_name) +] + +debuggerScriptCompileProc = popen(command) + print 'Compiling devtools.js...' command = spawned_compiler_command + [ @@ -492,6 +504,10 @@ def skip_dependents(module_name): print 'InjectedScriptSource.js compilation output:%s' % os.linesep, injectedScriptCompileOut errors_found |= hasErrors(injectedScriptCompileOut) +(debuggerScriptCompilerOut, _) = debuggerScriptCompileProc.communicate() +print 'DebuggerScript.js compilation output:%s' % os.linesep, debuggerScriptCompilerOut +errors_found |= hasErrors(debuggerScriptCompilerOut) + (devtoolsJSCompileOut, _) = devtoolsJSCompileProc.communicate() print 'devtools.js compilation output:%s' % os.linesep, devtoolsJSCompileOut errors_found |= hasErrors(devtoolsJSCompileOut)