WebKit Bugzilla
Attachment 361769 Details for
Bug 177484
: requestAnimationFrame should execute before the next frame
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-177484-20190211215136.patch (text/plain), 38.67 KB, created by
Said Abou-Hallawa
on 2019-02-11 21:51:37 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Said Abou-Hallawa
Created:
2019-02-11 21:51:37 PST
Size:
38.67 KB
patch
obsolete
>Subversion Revision: 241288 >diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog >index da5eece0d14cf1df21135d43f7d08485470f613b..88a7116b85590b60b466ae17a44b6a768b8659fe 100644 >--- a/Source/WTF/ChangeLog >+++ b/Source/WTF/ChangeLog >@@ -1,3 +1,15 @@ >+2019-02-11 Said Abou-Hallawa <sabouhallawa@apple.com> >+ >+ requestAnimationFrame should execute before the next frame >+ https://bugs.webkit.org/show_bug.cgi?id=177484 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add tracing signposts for requestAnimationFrame scheduling and flushing >+ the layers interval. >+ >+ * wtf/SystemTracing.h: >+ > 2019-02-11 Myles C. Maxfield <mmaxfield@apple.com> > > [Cocoa] Ask platform for generic font family mappings >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 18ee72539d313a2c7d68ca5782b3dc550a3e6eef..ac7587870c502409ba82257932cab766aea0e560 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,71 @@ >+2019-02-11 Said Abou-Hallawa <sabouhallawa@apple.com> >+ >+ requestAnimationFrame should execute before the next frame >+ https://bugs.webkit.org/show_bug.cgi?id=177484 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This change will fix two issues with animation timing: >+ >+ 1. Servicing requestAnimationFrame callbacks happens when the DisplayLink >+ fires. This may happen even if the frame is missed and no display is >+ committed. >+ >+ 2. Javascript may try to refresh the screen more than 60 FPS. WebCore >+ currently runs the layout and commits the layer although the graphical >+ system will throttle to 60 FPS at the end. >+ >+ These two issues can be fixed if we do the following: >+ >+ 1. DisplayMonitor callback will scheduleCompositingLayerFlush() instead >+ of servicing requestAnimationFrame callbacks. >+ >+ 2. When the page is about to be displayed, requestAnimationFrame callbacks >+ will be served. >+ >+ * Sources.txt: >+ * WebCore.xcodeproj/project.pbxproj: >+ * animation/DocumentAnimationScheduler.cpp: >+ (WebCore::DocumentAnimationScheduler::unscheduleWebAnimationsResolution): >+ (WebCore::DocumentAnimationScheduler::displayRefreshFired): >+ (WebCore::DocumentAnimationScheduler::scheduleScriptedAnimationResolution): Deleted. >+ * animation/DocumentAnimationScheduler.h: >+ * dom/Document.cpp: >+ (WebCore::Document::callRequestAnimationFrameCallbacks): >+ * dom/Document.h: >+ * dom/ScriptedAnimationController.cpp: >+ (WebCore::ScriptedAnimationController::callRequestAnimationFrameCallbacks): >+ (WebCore::ScriptedAnimationController::scheduleAnimation): >+ (WebCore::ScriptedAnimationController::animationTimerFired): >+ (WebCore::ScriptedAnimationController::documentAnimationSchedulerDidFire): >+ (WebCore::ScriptedAnimationController::serviceScriptedAnimations): Deleted. >+ * dom/ScriptedAnimationController.h: >+ * page/FrameTree.cpp: >+ * page/Page.cpp: >+ (WebCore::Page::processPreLayoutActions): >+ (WebCore::Page::processPreRenderActions): >+ (WebCore::Page::callRequestAnimationFrameCallbacks): >+ (WebCore::Page::renderScheduler): >+ (WebCore::Page::willDisplayPage): Deleted. >+ * page/Page.h: >+ * page/PageOverlayController.cpp: >+ (WebCore::PageOverlayController::didChangeViewExposedRect): >+ (WebCore::PageOverlayController::notifyFlushRequired): >+ * page/RenderScheduler.cpp: Added. >+ (WebCore::RenderScheduler::RenderScheduler): >+ (WebCore::RenderScheduler::scheduleRender): >+ (WebCore::RenderScheduler::startTimer): >+ (WebCore::RenderScheduler::clearTimer): >+ (WebCore::RenderScheduler::windowScreenDidChange): >+ (WebCore::RenderScheduler::createDisplayRefreshMonitor const): >+ (WebCore::RenderScheduler::displayRefreshFired): >+ * page/RenderScheduler.h: Added. >+ (WebCore::RenderScheduler::create): >+ * page/mac/ServicesOverlayController.mm: >+ (WebCore::ServicesOverlayController::Highlight::notifyFlushRequired): >+ * rendering/RenderLayerCompositor.cpp: >+ (WebCore::RenderLayerCompositor::scheduleLayerFlushNow): >+ > 2019-02-11 Myles C. Maxfield <mmaxfield@apple.com> > > [Cocoa] Ask platform for generic font family mappings >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 4d18124dfe1f44683aaa7077a6350b8acf9c7d07..14a2d858e3bfe94bf9fd392646f65957e5a20a9d 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,29 @@ >+2019-02-11 Said Abou-Hallawa <sabouhallawa@apple.com> >+ >+ requestAnimationFrame should execute before the next frame >+ https://bugs.webkit.org/show_bug.cgi?id=177484 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Handle the page-level rendering actions through two phases: one before >+ running the layout and the second is before committing the drawing. >+ >+ * WebProcess/WebPage/AcceleratedDrawingArea.cpp: >+ (WebKit::AcceleratedDrawingArea::updateBackingStoreState): >+ * WebProcess/WebPage/CoordinatedGraphics/CoordinatedLayerTreeHost.cpp: >+ (WebKit::CoordinatedLayerTreeHost::layerFlushTimerFired): >+ * WebProcess/WebPage/DrawingAreaImpl.cpp: >+ (WebKit::DrawingAreaImpl::display): >+ * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm: >+ (WebKit::RemoteLayerTreeDrawingArea::flushLayers): >+ * WebProcess/WebPage/WebPage.cpp: >+ (WebKit::WebPage::processPreLayoutActions): >+ (WebKit::WebPage::processPreRenderActions): >+ (WebKit::WebPage::willDisplayPage): Deleted. >+ * WebProcess/WebPage/WebPage.h: >+ * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm: >+ (WebKit::TiledCoreAnimationDrawingArea::flushLayers): >+ > 2019-02-11 Alex Christensen <achristensen@webkit.org> > > Remove noisy and unnecessary logs added in r241223 >diff --git a/Source/WebKitLegacy/mac/ChangeLog b/Source/WebKitLegacy/mac/ChangeLog >index 9a36586501c2dd39f6780b66302a81163da64397..7464782896ca030f957a4b67bf28374f22f7b583 100644 >--- a/Source/WebKitLegacy/mac/ChangeLog >+++ b/Source/WebKitLegacy/mac/ChangeLog >@@ -1,3 +1,15 @@ >+2019-02-11 Said Abou-Hallawa <sabouhallawa@apple.com> >+ >+ requestAnimationFrame should execute before the next frame >+ https://bugs.webkit.org/show_bug.cgi?id=177484 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Call Page::processPreLayoutActions() before flushing the layers. >+ >+ * WebView/WebView.mm: >+ (LayerFlushController::flushLayers): >+ > 2019-02-06 Andy Estes <aestes@apple.com> > > [Payment Request] It should be possible to require a phonetic name for shipping contacts >diff --git a/Source/WTF/wtf/SystemTracing.h b/Source/WTF/wtf/SystemTracing.h >index 105196ec48735fab52b3931511e00ed0c14fe15f..82101f4525ebc6f0bf7f1de251af3467fca9ceaa 100644 >--- a/Source/WTF/wtf/SystemTracing.h >+++ b/Source/WTF/wtf/SystemTracing.h >@@ -75,6 +75,11 @@ enum TracePointCode { > DisplayListRecordEnd, > DisplayRefreshDispatchingToMainThread, > >+ ScheduleRenderingUpdate, >+ TriggerRenderingUpdate, >+ RenderingUpdateStart, >+ RenderingUpdateEnd, >+ > WebKitRange = 10000, > WebHTMLViewPaintStart, > WebHTMLViewPaintEnd, >diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt >index 81cbc04ed3a7d7b57815b3577d442ab507121eca..29dc259aa233ad1663248a49efe4f5592e17342b 100644 >--- a/Source/WebCore/Sources.txt >+++ b/Source/WebCore/Sources.txt >@@ -1534,6 +1534,7 @@ page/ProcessWarming.cpp > page/Quirks.cpp > page/RemoteDOMWindow.cpp > page/RemoteFrame.cpp >+page/RenderScheduler.cpp > page/ResourceUsageOverlay.cpp > page/ResourceUsageThread.cpp > page/RuntimeEnabledFeatures.cpp >diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >index 0273cbbabf40606b43b16487110be5b2f56ab477..df9e7ff6b6b115581e86d9cb03643b576baccf6c 100644 >--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj >+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj >@@ -1779,6 +1779,7 @@ > 5550CB421E955E3C00111AA0 /* ImageTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 5550CB411E955E3C00111AA0 /* ImageTypes.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 555130011E7CCCCB00A69E38 /* DecodingOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 555130001E7CCCCA00A69E38 /* DecodingOptions.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 555B87ED1CAAF0AB00349425 /* ImageDecoderCG.h in Headers */ = {isa = PBXBuildFile; fileRef = 555B87EB1CAAF0AB00349425 /* ImageDecoderCG.h */; }; >+ 556C7C4B22123997009B06CA /* RenderScheduler.h in Headers */ = {isa = PBXBuildFile; fileRef = 556C7C4722123942009B06CA /* RenderScheduler.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 5576A5651D88A70800CCC04C /* ImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 5576A5631D88A70800CCC04C /* ImageFrame.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 55A336F91D821E3C0022C4C7 /* ImageBackingStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 55A336F81D821E3C0022C4C7 /* ImageBackingStore.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 55AF14E61EAAC59B0026EEAA /* UTIRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 55AF14E41EAAC59B0026EEAA /* UTIRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -8642,6 +8643,8 @@ > 555130001E7CCCCA00A69E38 /* DecodingOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DecodingOptions.h; sourceTree = "<group>"; }; > 555B87EA1CAAF0AB00349425 /* ImageDecoderCG.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageDecoderCG.cpp; sourceTree = "<group>"; }; > 555B87EB1CAAF0AB00349425 /* ImageDecoderCG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageDecoderCG.h; sourceTree = "<group>"; }; >+ 556C7C4722123942009B06CA /* RenderScheduler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderScheduler.h; sourceTree = "<group>"; }; >+ 556C7C4922123943009B06CA /* RenderScheduler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderScheduler.cpp; sourceTree = "<group>"; }; > 5576A5621D88A70800CCC04C /* ImageFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ImageFrame.cpp; sourceTree = "<group>"; }; > 5576A5631D88A70800CCC04C /* ImageFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageFrame.h; sourceTree = "<group>"; }; > 5597FCCB2076C06800D35BB0 /* GlyphDisplayListCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlyphDisplayListCache.h; sourceTree = "<group>"; }; >@@ -20481,6 +20484,8 @@ > 46BCBBC3208500A700710638 /* RemoteDOMWindow.idl */, > 46B9518C207D632900A7D2DD /* RemoteFrame.cpp */, > 46B95192207D632E00A7D2DD /* RemoteFrame.h */, >+ 556C7C4922123943009B06CA /* RenderScheduler.cpp */, >+ 556C7C4722123942009B06CA /* RenderScheduler.h */, > A5071E821C56D079009951BE /* ResourceUsageData.h */, > ADBAD6EC1BCDD95000381325 /* ResourceUsageOverlay.cpp */, > ADBAD6ED1BCDD95000381325 /* ResourceUsageOverlay.h */, >@@ -31286,6 +31291,7 @@ > 1479FAF0109AE37500DED655 /* RenderRubyBase.h in Headers */, > 1479FAF2109AE37500DED655 /* RenderRubyRun.h in Headers */, > 1479FAF4109AE37500DED655 /* RenderRubyText.h in Headers */, >+ 556C7C4B22123997009B06CA /* RenderScheduler.h in Headers */, > BC3BE9940E9C1C7C00835588 /* RenderScrollbar.h in Headers */, > BC3BE9950E9C1C7C00835588 /* RenderScrollbarPart.h in Headers */, > BC3BE9990E9C1E5D00835588 /* RenderScrollbarTheme.h in Headers */, >diff --git a/Source/WebCore/animation/DocumentAnimationScheduler.cpp b/Source/WebCore/animation/DocumentAnimationScheduler.cpp >index ada330a67cd4c3dda8b235c1781c7406091c8286..24e5cc6d387c38011b92504f7dcddb22b1f728ac 100644 >--- a/Source/WebCore/animation/DocumentAnimationScheduler.cpp >+++ b/Source/WebCore/animation/DocumentAnimationScheduler.cpp >@@ -67,15 +67,7 @@ bool DocumentAnimationScheduler::scheduleWebAnimationsResolution() > void DocumentAnimationScheduler::unscheduleWebAnimationsResolution() > { > m_scheduledWebAnimationsResolution = false; >- >- if (!m_scheduledScriptedAnimationResolution) >- DisplayRefreshMonitorManager::sharedManager().unregisterClient(*this); >-} >- >-bool DocumentAnimationScheduler::scheduleScriptedAnimationResolution() >-{ >- m_scheduledScriptedAnimationResolution = true; >- return DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this); >+ DisplayRefreshMonitorManager::sharedManager().unregisterClient(*this); > } > > void DocumentAnimationScheduler::displayRefreshFired() >@@ -94,12 +86,6 @@ void DocumentAnimationScheduler::displayRefreshFired() > m_document->timeline().documentAnimationSchedulerDidFire(); > } > >- if (m_scheduledScriptedAnimationResolution) { >- m_scheduledScriptedAnimationResolution = false; >- if (auto* scriptedAnimationController = m_document->scriptedAnimationController()) >- scriptedAnimationController->documentAnimationSchedulerDidFire(); >- } >- > m_isFiring = false; > } > >diff --git a/Source/WebCore/animation/DocumentAnimationScheduler.h b/Source/WebCore/animation/DocumentAnimationScheduler.h >index 98e88c51bdadd9d6db0a312d0a2767b18c57c83d..c2a1c9a98069f81654e433f32cfc585bb5817d61 100644 >--- a/Source/WebCore/animation/DocumentAnimationScheduler.h >+++ b/Source/WebCore/animation/DocumentAnimationScheduler.h >@@ -59,7 +59,6 @@ private: > > RefPtr<Document> m_document; > bool m_scheduledWebAnimationsResolution { false }; >- bool m_scheduledScriptedAnimationResolution { false }; > bool m_isFiring { false }; > Seconds m_lastTimestamp { 0_s }; > >diff --git a/Source/WebCore/dom/Document.cpp b/Source/WebCore/dom/Document.cpp >index 050555ba9491f47ca0bb4dfdaaa193d9ac54edf9..05920e9738bf8404c7d5723e2c9a4daefb20dc06 100644 >--- a/Source/WebCore/dom/Document.cpp >+++ b/Source/WebCore/dom/Document.cpp >@@ -6293,6 +6293,12 @@ void Document::resumeScriptedAnimationControllerCallbacks() > m_scriptedAnimationController->resume(); > } > >+void Document::callRequestAnimationFrameCallbacks() >+{ >+ if (m_scriptedAnimationController) >+ m_scriptedAnimationController->callRequestAnimationFrameCallbacks(domWindow()->nowTimestamp()); >+} >+ > void Document::windowScreenDidChange(PlatformDisplayID displayID) > { > #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >diff --git a/Source/WebCore/dom/Document.h b/Source/WebCore/dom/Document.h >index 9bdf1b8d879f513e8c233e5c702da1fb15e2da4b..85bfe6b603618e6f117fa4aef0a80da4b3e72e9a 100644 >--- a/Source/WebCore/dom/Document.h >+++ b/Source/WebCore/dom/Document.h >@@ -1067,7 +1067,9 @@ public: > ScriptedAnimationController* scriptedAnimationController() { return m_scriptedAnimationController.get(); } > void suspendScriptedAnimationControllerCallbacks(); > void resumeScriptedAnimationControllerCallbacks(); >- >+ >+ void callRequestAnimationFrameCallbacks(); >+ > void windowScreenDidChange(PlatformDisplayID); > > void finishedParsing(); >diff --git a/Source/WebCore/dom/ScriptedAnimationController.cpp b/Source/WebCore/dom/ScriptedAnimationController.cpp >index e88bb90cfbae17ad6d006e01b64204230d0fe801..96ad77512e491f8724d9d9deeea2783d0d9ae27f 100644 >--- a/Source/WebCore/dom/ScriptedAnimationController.cpp >+++ b/Source/WebCore/dom/ScriptedAnimationController.cpp >@@ -189,7 +189,7 @@ void ScriptedAnimationController::cancelCallback(CallbackId id) > } > } > >-void ScriptedAnimationController::serviceScriptedAnimations(double timestamp) >+void ScriptedAnimationController::callRequestAnimationFrameCallbacks(double timestamp) > { > if (!m_callbacks.size() || m_suspendCount || !requestAnimationFrameEnabled()) > return; >@@ -262,8 +262,10 @@ void ScriptedAnimationController::scheduleAnimation() > > #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) > if (!m_isUsingTimer && !isThrottled()) { >- if (m_document->animationScheduler().scheduleScriptedAnimationResolution()) >+ if (auto* page = this->page()) { >+ page->renderScheduler().scheduleRender(); > return; >+ } > > m_isUsingTimer = true; > } >@@ -292,14 +294,14 @@ void ScriptedAnimationController::scheduleAnimation() > void ScriptedAnimationController::animationTimerFired() > { > m_lastAnimationFrameTimestamp = m_document->domWindow()->nowTimestamp(); >- serviceScriptedAnimations(m_lastAnimationFrameTimestamp); >+ callRequestAnimationFrameCallbacks(m_lastAnimationFrameTimestamp); > } > > #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) > void ScriptedAnimationController::documentAnimationSchedulerDidFire() > { > // We obtain the time from the animation scheduler so that we use the same timestamp as the DocumentTimeline. >- serviceScriptedAnimations(m_document->animationScheduler().lastTimestamp().seconds()); >+ callRequestAnimationFrameCallbacks(m_document->animationScheduler().lastTimestamp().seconds()); > } > #endif > >diff --git a/Source/WebCore/dom/ScriptedAnimationController.h b/Source/WebCore/dom/ScriptedAnimationController.h >index 7ab8ec14a007b735f850834cc4dc123e9e6a9f91..835cfad7902c7beefe36f552d9a804377e289484 100644 >--- a/Source/WebCore/dom/ScriptedAnimationController.h >+++ b/Source/WebCore/dom/ScriptedAnimationController.h >@@ -52,7 +52,7 @@ public: > > CallbackId registerCallback(Ref<RequestAnimationFrameCallback>&&); > void cancelCallback(CallbackId); >- void serviceScriptedAnimations(double timestamp); >+ void callRequestAnimationFrameCallbacks(double timestamp); > > void suspend(); > void resume(); >diff --git a/Source/WebCore/page/FrameTree.cpp b/Source/WebCore/page/FrameTree.cpp >index d63f861a780528f720334b3dbb81f7bb08c19a9c..24511da24b0da9e52b55781825fba5775a82021a 100644 >--- a/Source/WebCore/page/FrameTree.cpp >+++ b/Source/WebCore/page/FrameTree.cpp >@@ -23,6 +23,7 @@ > > #include "Document.h" > #include "Frame.h" >+#include "FrameLoader.h" > #include "FrameView.h" > #include "HTMLFrameOwnerElement.h" > #include "Page.h" >diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp >index 690735d4f0cb20c702b2b7b47d48bb2a322e9276..ab8c3973582ed639617cdc88f3116b4349f97398 100644 >--- a/Source/WebCore/page/Page.cpp >+++ b/Source/WebCore/page/Page.cpp >@@ -1112,13 +1112,6 @@ void Page::didFinishLoad() > m_performanceMonitor->didFinishLoad(); > } > >-void Page::willDisplayPage() >-{ >-#if ENABLE(INTERSECTION_OBSERVER) >- updateIntersectionObservations(); >-#endif >-} >- > bool Page::isOnlyNonUtilityPage() const > { > return !isUtilityPage() && nonUtilityPageCount == 1; >@@ -1260,6 +1253,18 @@ void Page::removeActivityStateChangeObserver(ActivityStateChangeObserver& observ > m_activityStateChangeObservers.remove(&observer); > } > >+void Page::processPreLayoutActions() >+{ >+ callRequestAnimationFrameCallbacks(); >+} >+ >+void Page::processPreRenderActions() >+{ >+#if ENABLE(INTERSECTION_OBSERVER) >+ updateIntersectionObservations(); >+#endif >+} >+ > #if ENABLE(INTERSECTION_OBSERVER) > void Page::addDocumentNeedingIntersectionObservationUpdate(Document& document) > { >@@ -1286,6 +1291,14 @@ void Page::scheduleForcedIntersectionObservationUpdate(Document& document) > } > #endif > >+void Page::callRequestAnimationFrameCallbacks() >+{ >+ for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { >+ if (frame->document()) >+ frame->document()->callRequestAnimationFrameCallbacks(); >+ } >+} >+ > void Page::suspendScriptedAnimations() > { > m_scriptedAnimationsSuspended = true; >@@ -2815,6 +2828,13 @@ void Page::didChangeMainDocument() > #endif > } > >+RenderScheduler& Page::renderScheduler() >+{ >+ if (!m_renderScheduler) >+ m_renderScheduler = RenderScheduler::create(*this); >+ return *m_renderScheduler; >+} >+ > void Page::forEachDocument(const Function<void(Document&)>& functor) > { > for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { >diff --git a/Source/WebCore/page/Page.h b/Source/WebCore/page/Page.h >index 9464a8ddaf07a543f0b868ab083649caff7204b7..6232c71f7e519b931541f8a7f57614149450d37c 100644 >--- a/Source/WebCore/page/Page.h >+++ b/Source/WebCore/page/Page.h >@@ -32,6 +32,7 @@ > #include "Pagination.h" > #include "RTCController.h" > #include "Region.h" >+#include "RenderScheduler.h" > #include "ScrollTypes.h" > #include "Supplementable.h" > #include "Timer.h" >@@ -261,6 +262,8 @@ public: > > PerformanceMonitor* performanceMonitor() { return m_performanceMonitor.get(); } > >+ RenderScheduler& renderScheduler(); >+ > ValidationMessageClient* validationMessageClient() const { return m_validationMessageClient.get(); } > void updateValidationBubbleStateIfNeeded(); > >@@ -333,8 +336,6 @@ public: > void didStartProvisionalLoad(); > void didFinishLoad(); // Called when the load has been committed in the main frame. > >- WEBCORE_EXPORT void willDisplayPage(); >- > // The view scale factor is multiplied into the page scale factor by all > // callers of setPageScaleFactor. > WEBCORE_EXPORT void setViewScaleFactor(float); >@@ -464,12 +465,17 @@ public: > WEBCORE_EXPORT void addActivityStateChangeObserver(ActivityStateChangeObserver&); > WEBCORE_EXPORT void removeActivityStateChangeObserver(ActivityStateChangeObserver&); > >+ WEBCORE_EXPORT void processPreLayoutActions(); >+ WEBCORE_EXPORT void processPreRenderActions(); >+ > #if ENABLE(INTERSECTION_OBSERVER) > void addDocumentNeedingIntersectionObservationUpdate(Document&); > void scheduleForcedIntersectionObservationUpdate(Document&); > void updateIntersectionObservations(); > #endif > >+ void callRequestAnimationFrameCallbacks(); >+ > WEBCORE_EXPORT void suspendScriptedAnimations(); > WEBCORE_EXPORT void resumeScriptedAnimations(); > bool scriptedAnimationsSuspended() const { return m_scriptedAnimationsSuspended; } >@@ -860,6 +866,8 @@ private: > int m_headerHeight { 0 }; > int m_footerHeight { 0 }; > >+ std::unique_ptr<RenderScheduler> m_renderScheduler; >+ > HashSet<RenderObject*> m_relevantUnpaintedRenderObjects; > Region m_topRelevantPaintedRegion; > Region m_bottomRelevantPaintedRegion; >diff --git a/Source/WebCore/page/PageOverlayController.cpp b/Source/WebCore/page/PageOverlayController.cpp >index 86a68529581683223cf02cedf865930e7661f5c3..17181cb85593012b059fa51ee38bc9b198af365e 100644 >--- a/Source/WebCore/page/PageOverlayController.cpp >+++ b/Source/WebCore/page/PageOverlayController.cpp >@@ -317,7 +317,7 @@ void PageOverlayController::didChangeDeviceScaleFactor() > > void PageOverlayController::didChangeViewExposedRect() > { >- m_page.chrome().client().scheduleCompositingLayerFlush(); >+ m_page.renderScheduler().scheduleRender(); > } > > void PageOverlayController::didScrollFrame(Frame& frame) >@@ -411,7 +411,7 @@ float PageOverlayController::deviceScaleFactor() const > > void PageOverlayController::notifyFlushRequired(const WebCore::GraphicsLayer*) > { >- m_page.chrome().client().scheduleCompositingLayerFlush(); >+ m_page.renderScheduler().scheduleRender(); > } > > void PageOverlayController::didChangeOverlayFrame(PageOverlay& overlay) >diff --git a/Source/WebCore/page/RenderScheduler.cpp b/Source/WebCore/page/RenderScheduler.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..1214334b626ce74a8f5667300ab77e8136685e52 >--- /dev/null >+++ b/Source/WebCore/page/RenderScheduler.cpp >@@ -0,0 +1,96 @@ >+/* >+ * Copyright (C) 2019 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "RenderScheduler.h" >+ >+#include "Chrome.h" >+#include "ChromeClient.h" >+#include "DisplayRefreshMonitorManager.h" >+#include "Page.h" >+#include <wtf/SystemTracing.h> >+ >+namespace WebCore { >+ >+RenderScheduler::RenderScheduler(Page& page) >+ : m_page(page) >+{ >+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >+ windowScreenDidChange(page.chrome().displayID()); >+#endif >+} >+ >+void RenderScheduler::scheduleRender() >+{ >+ if (m_scheduled) >+ return; >+ >+ tracePoint(ScheduleRenderingUpdate); >+ >+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >+ if (!DisplayRefreshMonitorManager::sharedManager().scheduleAnimation(*this)) >+#endif >+ startTimer(Seconds(1.0 / 60)); >+ >+ m_scheduled = true; >+} >+ >+void RenderScheduler::startTimer(Seconds delay) >+{ >+ ASSERT(!m_refreshTimer); >+ m_refreshTimer = std::make_unique<Timer>(*this, &RenderScheduler::displayRefreshFired); >+ m_refreshTimer->startOneShot(delay); >+} >+ >+void RenderScheduler::clearTimer() >+{ >+ m_refreshTimer = nullptr; >+} >+ >+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >+void RenderScheduler::windowScreenDidChange(PlatformDisplayID displayID) >+{ >+ DisplayRefreshMonitorManager::sharedManager().windowScreenDidChange(displayID, *this); >+} >+ >+RefPtr<DisplayRefreshMonitor> RenderScheduler::createDisplayRefreshMonitor(PlatformDisplayID displayID) const >+{ >+ if (auto monitor = m_page.chrome().client().createDisplayRefreshMonitor(displayID)) >+ return monitor; >+ >+ return DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(displayID); >+} >+#endif >+ >+void RenderScheduler::displayRefreshFired() >+{ >+ tracePoint(TriggerRenderingUpdate); >+ >+ m_page.chrome().client().scheduleCompositingLayerFlush(); >+ clearTimer(); >+ m_scheduled = false; >+} >+ >+} >diff --git a/Source/WebCore/page/RenderScheduler.h b/Source/WebCore/page/RenderScheduler.h >new file mode 100644 >index 0000000000000000000000000000000000000000..4ed91d02042798cadb7728ee849bfa931f6b4688 >--- /dev/null >+++ b/Source/WebCore/page/RenderScheduler.h >@@ -0,0 +1,71 @@ >+/* >+ * Copyright (C) 2019 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#include "DisplayRefreshMonitorClient.h" >+#include <wtf/Seconds.h> >+ >+namespace WebCore { >+ >+class Page; >+class Timer; >+ >+class RenderScheduler >+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >+ : public DisplayRefreshMonitorClient >+#endif >+{ >+ WTF_MAKE_FAST_ALLOCATED; >+public: >+ static std::unique_ptr<RenderScheduler> create(Page& page) >+ { >+ return std::make_unique<RenderScheduler>(page); >+ } >+ >+ RenderScheduler(Page&); >+ void scheduleRender(); >+ >+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >+ void windowScreenDidChange(PlatformDisplayID); >+#endif >+ >+private: >+#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) >+ RefPtr<DisplayRefreshMonitor> createDisplayRefreshMonitor(PlatformDisplayID) const final; >+ void displayRefreshFired() final; >+#else >+ void displayRefreshFired(); >+#endif >+ >+ void startTimer(Seconds); >+ void clearTimer(); >+ >+ Page& m_page; >+ std::unique_ptr<Timer> m_refreshTimer; >+ bool m_scheduled { false }; >+}; >+ >+} >diff --git a/Source/WebCore/page/mac/ServicesOverlayController.mm b/Source/WebCore/page/mac/ServicesOverlayController.mm >index c96bed10869f675dbf5145f9f3e0f4123f02fbe7..a0c9967a678b61991578a85984a6dae9b29061b6 100644 >--- a/Source/WebCore/page/mac/ServicesOverlayController.mm >+++ b/Source/WebCore/page/mac/ServicesOverlayController.mm >@@ -121,7 +121,7 @@ void ServicesOverlayController::Highlight::notifyFlushRequired(const GraphicsLay > if (!m_controller) > return; > >- m_controller->page().chrome().client().scheduleCompositingLayerFlush(); >+ m_controller->page().renderScheduler().scheduleRender(); > } > > void ServicesOverlayController::Highlight::paintContents(const GraphicsLayer*, GraphicsContext& graphicsContext, GraphicsLayerPaintingPhase, const FloatRect&, GraphicsLayerPaintBehavior) >diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp >index 19dab8a341386ccb840165664ff5f85570db983e..83f0bb9e1c11f6a16458fdde5796450331d2b7ec 100644 >--- a/Source/WebCore/rendering/RenderLayerCompositor.cpp >+++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp >@@ -430,7 +430,7 @@ void RenderLayerCompositor::notifyFlushRequired(const GraphicsLayer* layer) > void RenderLayerCompositor::scheduleLayerFlushNow() > { > m_hasPendingLayerFlush = false; >- page().chrome().client().scheduleCompositingLayerFlush(); >+ page().renderScheduler().scheduleRender(); > } > > void RenderLayerCompositor::scheduleLayerFlush(bool canThrottle) >diff --git a/Source/WebKit/WebProcess/WebPage/AcceleratedDrawingArea.cpp b/Source/WebKit/WebProcess/WebPage/AcceleratedDrawingArea.cpp >index edfe46b218e050fc02edfb2b976f0dfdedc0de7f..2b554167a66b0e42f03a5843691418d0d3f71731 100644 >--- a/Source/WebKit/WebProcess/WebPage/AcceleratedDrawingArea.cpp >+++ b/Source/WebKit/WebProcess/WebPage/AcceleratedDrawingArea.cpp >@@ -256,7 +256,7 @@ void AcceleratedDrawingArea::updateBackingStoreState(uint64_t stateID, bool resp > m_webPage.layoutIfNeeded(); > m_webPage.flushPendingEditorStateUpdate(); > m_webPage.scrollMainFrameIfNotAtMaxScrollPosition(scrollOffset); >- m_webPage.willDisplayPage(); >+ m_webPage.processPreRenderActions(); > > if (m_layerTreeHost) > m_layerTreeHost->sizeDidChange(m_webPage.size()); >diff --git a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/CoordinatedLayerTreeHost.cpp b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/CoordinatedLayerTreeHost.cpp >index 1520f520dac98e43e45b7e0637571b930258ccac..9288e4dbf9398641f107987db7187688668088c3 100644 >--- a/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/CoordinatedLayerTreeHost.cpp >+++ b/Source/WebKit/WebProcess/WebPage/CoordinatedGraphics/CoordinatedLayerTreeHost.cpp >@@ -189,7 +189,7 @@ void CoordinatedLayerTreeHost::layerFlushTimerFired() > > m_coordinator.syncDisplayState(); > m_webPage.flushPendingEditorStateUpdate(); >- m_webPage.willDisplayPage(); >+ m_webPage.processPreRenderActions(); > > if (!m_isValid || !m_coordinator.rootCompositingLayer()) > return; >diff --git a/Source/WebKit/WebProcess/WebPage/DrawingAreaImpl.cpp b/Source/WebKit/WebProcess/WebPage/DrawingAreaImpl.cpp >index 6a86b002b86f42a01cf48e5df6900acf58f831a9..1affa3239a13c744137a941d9bc552703f5b7744 100644 >--- a/Source/WebKit/WebProcess/WebPage/DrawingAreaImpl.cpp >+++ b/Source/WebKit/WebProcess/WebPage/DrawingAreaImpl.cpp >@@ -406,7 +406,7 @@ void DrawingAreaImpl::display(UpdateInfo& updateInfo) > if (m_layerTreeHost) > return; > >- m_webPage.willDisplayPage(); >+ m_webPage.processPreRenderActions(); > updateInfo.viewSize = m_webPage.size(); > updateInfo.deviceScaleFactor = m_webPage.corePage()->deviceScaleFactor(); > >diff --git a/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm b/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm >index ce2d5cbdece8d9dc5177168df056109a30ee89b2..e65cfe28c8f2b873839e5f4ac57d85097ad7b78e 100644 >--- a/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm >+++ b/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDrawingArea.mm >@@ -347,8 +347,9 @@ void RemoteLayerTreeDrawingArea::flushLayers() > RemoteLayerBackingStoreCollection& backingStoreCollection = m_remoteLayerTreeContext->backingStoreCollection(); > backingStoreCollection.willFlushLayers(); > >+ m_webPage.processPreLayoutActions(); > m_webPage.layoutIfNeeded(); >- m_webPage.willDisplayPage(); >+ m_webPage.processPreRenderActions(); > > FloatRect visibleRect(FloatPoint(), m_viewSize); > if (m_scrolledViewExposedRect) >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.cpp b/Source/WebKit/WebProcess/WebPage/WebPage.cpp >index 20a55283a49864a4cc5afc0cccd8bf167165e71d..418cab35f18f3556c99963bb2716d131c76cb289 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.cpp >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.cpp >@@ -3569,9 +3569,14 @@ void WebPage::didFlushLayerTreeAtTime(MonotonicTime timestamp) > } > #endif > >-void WebPage::willDisplayPage() >+void WebPage::processPreLayoutActions() > { >- m_page->willDisplayPage(); >+ m_page->processPreLayoutActions(); >+} >+ >+void WebPage::processPreRenderActions() >+{ >+ m_page->processPreRenderActions(); > } > > WebInspector* WebPage::inspector(LazyCreationPolicy behavior) >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h >index a22ed87ec7c2a29d53d671f20fde2de315b31dd7..357af5197322da471f37734507cd649dcc3e8e7f 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.h >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.h >@@ -300,7 +300,8 @@ public: > void didFlushLayerTreeAtTime(MonotonicTime); > #endif > >- void willDisplayPage(); >+ void processPreLayoutActions(); >+ void processPreRenderActions(); > > enum class LazyCreationPolicy { UseExistingOnly, CreateIfNeeded }; > >diff --git a/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm b/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm >index 324d063162332b8d4f66621679b05271129478c2..4968a36a3c57e2c0884907b92dd8327d17f60b9c 100644 >--- a/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm >+++ b/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm >@@ -59,6 +59,7 @@ > #import <pal/spi/cocoa/QuartzCoreSPI.h> > #import <wtf/MachSendRight.h> > #import <wtf/MainThread.h> >+#import <wtf/SystemTracing.h> > > #if ENABLE(ASYNC_SCROLLING) > #import <WebCore/AsyncScrollingCoordinator.h> >@@ -459,12 +460,15 @@ void TiledCoreAnimationDrawingArea::flushLayers() > if (layerTreeStateIsFrozen()) > return; > >+ TraceScope traceScope(RenderingUpdateStart, RenderingUpdateEnd); >+ > @autoreleasepool { > scaleViewToFitDocumentIfNeeded(); > >+ m_webPage.processPreLayoutActions(); > m_webPage.layoutIfNeeded(); > m_webPage.flushPendingEditorStateUpdate(); >- m_webPage.willDisplayPage(); >+ m_webPage.processPreRenderActions(); > > updateIntrinsicContentSizeIfNeeded(); > >diff --git a/Source/WebKitLegacy/mac/WebView/WebView.mm b/Source/WebKitLegacy/mac/WebView/WebView.mm >index 480f88502cb5bd55d7ea6d6702a6c6976520c018..f874c207086401096f800d584ae20a8e55d5704c 100644 >--- a/Source/WebKitLegacy/mac/WebView/WebView.mm >+++ b/Source/WebKitLegacy/mac/WebView/WebView.mm >@@ -9348,6 +9348,9 @@ bool LayerFlushController::flushLayers() > [m_webView _synchronizeCustomFixedPositionLayoutRect]; > #endif > >+ Page* page = m_webView->_private->page; >+ page->processPreLayoutActions(); >+ > [m_webView _viewWillDrawInternal]; > > if ([m_webView _flushCompositingChanges]) { >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 41ae710ca7dd31edd67ccfe58b884936d14da19b..9a57e72625442e3c17c8739b6cb7ea292119cadc 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,12 @@ >+2019-02-11 Said Abou-Hallawa <sabouhallawa@apple.com> >+ >+ requestAnimationFrame should execute before the next frame >+ https://bugs.webkit.org/show_bug.cgi?id=177484 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * Tracing/SystemTracePoints.plist: >+ > 2019-02-11 Myles C. Maxfield <mmaxfield@apple.com> > > [Cocoa] Ask platform for generic font family mappings >diff --git a/Tools/Tracing/SystemTracePoints.plist b/Tools/Tracing/SystemTracePoints.plist >index 0e1a6783866dc2df5dbadb82eb375e8686330769..1bb29410fc9c3849e7c0b323c8a34448da75c729 100644 >--- a/Tools/Tracing/SystemTracePoints.plist >+++ b/Tools/Tracing/SystemTracePoints.plist >@@ -202,8 +202,42 @@ > <key>Component</key> > <string>47</string> > <key>Code</key> >+ <string>5025</string> >+ </dict> >+ >+ >+ <dict> >+ <key>Name</key> >+ <string>Schedule display refresh</string> >+ <key>Type</key> >+ <string>Impulse</string> >+ <key>Component</key> >+ <string>47</string> >+ <key>Code</key> > <string>5026</string> > </dict> >+ <dict> >+ <key>Name</key> >+ <string>Trigger display refresh</string> >+ <key>Type</key> >+ <string>Impulse</string> >+ <key>Component</key> >+ <string>47</string> >+ <key>Code</key> >+ <string>5027</string> >+ </dict> >+ <dict> >+ <key>Name</key> >+ <string>Update rendering</string> >+ <key>Type</key> >+ <string>Interval</string> >+ <key>Component</key> >+ <string>47</string> >+ <key>CodeBegin</key> >+ <string>5028</string> >+ <key>CodeEnd</key> >+ <string>5029</string> >+ </dict> > <dict> > <key>Name</key> > <string>Paint WebHTMLView</string>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 177484
:
356578
|
361620
|
361621
|
361622
|
361623
|
361718
|
361727
|
361743
|
361749
|
361750
|
361752
|
361756
|
361760
|
361764
|
361769
|
361775
|
361776
|
361779
|
361783
|
361947
|
361963
|
361970
|
361973
|
361974
|
362845
|
362847
|
362848
|
362849
|
362850
|
362851
|
363268
|
363274
|
363286
|
363295
|
363309
|
363313
|
363417
|
363419
|
363420
|
363831
|
363846
|
363850
|
363857
|
363862
|
363940
|
366413
|
366416
|
366417
|
366418
|
366427
|
366731
|
366736
|
366740
|
366745
|
367083
|
367091
|
367145
|
367151
|
367160
|
367186