author | Andreea Pavel <apavel@mozilla.com> |
Sun, 10 Jun 2018 12:51:28 +0300 | |
changeset 422285 | 7d4819bfa8c3050573e499f87c9ba6d55354d2ae |
parent 422284 | b7974b9e7221e1bdf06ced38b6a8094465928549 (current diff) |
parent 422112 | 6ecc243768757b4db83d2467e47078e12bc6c17b (diff) |
child 422286 | eaf6dbd7b716ccf51c291bcb3749b07353bdf854 |
push id | 34122 |
push user | ebalazs@mozilla.com |
push date | Mon, 11 Jun 2018 09:37:00 +0000 |
treeherder | mozilla-central@9941eb8c3b29 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 62.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
testing/raptor/raptor/tests/raptor-firefox-tp6.ini | file | annotate | diff | comparison | revisions | |
toolkit/modules/DateTimePickerHelper.jsm | file | annotate | diff | comparison | revisions |
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -100,17 +100,17 @@ XPCOMUtils.defineLazyModuleGetters(this, BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm", BrowserErrorReporter: "resource:///modules/BrowserErrorReporter.jsm", BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm", BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm", BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm", ContentClick: "resource:///modules/ContentClick.jsm", ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm", CustomizableUI: "resource:///modules/CustomizableUI.jsm", - DateTimePickerHelper: "resource://gre/modules/DateTimePickerHelper.jsm", + DateTimePickerParent: "resource://gre/modules/DateTimePickerParent.jsm", ExtensionsUI: "resource:///modules/ExtensionsUI.jsm", Feeds: "resource:///modules/Feeds.jsm", FileSource: "resource://gre/modules/L10nRegistry.jsm", FormValidationHandler: "resource:///modules/FormValidationHandler.jsm", FxAccounts: "resource://gre/modules/FxAccounts.jsm", HybridContentTelemetry: "resource://gre/modules/HybridContentTelemetry.jsm", Integration: "resource://gre/modules/Integration.jsm", L10nRegistry: "resource://gre/modules/L10nRegistry.jsm", @@ -1043,17 +1043,17 @@ BrowserGlue.prototype = { this._resetProfileNotification("uninstall"); } } } this._checkForOldBuildUpdates(); AutoCompletePopup.init(); - DateTimePickerHelper.init(); + DateTimePickerParent.init(); // Check if Sync is configured if (Services.prefs.prefHasUserValue("services.sync.username")) { WeaveService.init(); } PageThumbs.init(); NewTabUtils.init(); @@ -1107,17 +1107,17 @@ BrowserGlue.prototype = { if (Object.prototype.hasOwnProperty.call(this, "pingCentre")) { this.pingCentre.uninit(); } PageThumbs.uninit(); NewTabUtils.uninit(); AboutPrivateBrowsingHandler.uninit(); AutoCompletePopup.uninit(); - DateTimePickerHelper.uninit(); + DateTimePickerParent.uninit(); // Browser errors are only collected on Nightly if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_DATA_REPORTING) { this.browserErrorReporter.uninit(); } Normandy.uninit();
--- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -456,17 +456,17 @@ EventListenerManager::ProcessApzAwareEve if (!doc) { if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) { if (nsPIDOMWindowInner* window = helper->GetOwner()) { doc = window->GetExtantDoc(); } } } - if (doc && nsDisplayListBuilder::LayerEventRegionsEnabled()) { + if (doc && gfxPlatform::AsyncPanZoomEnabled()) { nsIPresShell* ps = doc->GetShell(); if (ps) { nsIFrame* f = ps->GetRootFrame(); if (f) { f->SchedulePaint(); } } }
--- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -2464,17 +2464,17 @@ APZCTreeManager::GetTargetAPZC(const Scr CompositorHitTestInfo* aOutHitResult, HitTestingTreeNodeAutoLock* aOutScrollbarNode) { RecursiveMutexAutoLock lock(mTreeLock); CompositorHitTestInfo hitResult = CompositorHitTestInfo::eInvisibleToHitTest; HitTestingTreeNode* scrollbarNode = nullptr; RefPtr<AsyncPanZoomController> target; - if (gfx::gfxVars::UseWebRender() && gfxPrefs::WebRenderHitTest()) { + if (gfx::gfxVars::UseWebRender()) { target = GetAPZCAtPointWR(aPoint, &hitResult, &scrollbarNode); } else { target = GetAPZCAtPoint(mRootNode, aPoint, &hitResult, &scrollbarNode); } if (aOutHitResult) { *aOutHitResult = hitResult; }
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h +++ b/gfx/layers/apz/test/gtest/APZTestCommon.h @@ -11,16 +11,17 @@ * Defines a set of mock classes and utility functions/classes for * writing APZ gtests. */ #include "gtest/gtest.h" #include "gmock/gmock.h" #include "mozilla/Attributes.h" +#include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform #include "mozilla/layers/GeckoContentController.h" #include "mozilla/layers/CompositorBridgeParent.h" #include "mozilla/layers/LayerMetricsWrapper.h" #include "mozilla/layers/APZThreadUtils.h" #include "mozilla/TypedEnumBits.h" #include "mozilla/UniquePtr.h" #include "apz/src/APZCTreeManager.h" @@ -57,41 +58,47 @@ CreateSingleTouchData(int32_t aIdentifie // Convenience wrapper for CreateSingleTouchData() that takes loose coordinates. SingleTouchData CreateSingleTouchData(int32_t aIdentifier, ScreenIntCoord aX, ScreenIntCoord aY) { return CreateSingleTouchData(aIdentifier, ScreenIntPoint(aX, aY)); } -template<class T> -class ScopedGfxPref { +template<class SetArg, class Storage> +class ScopedGfxSetting { public: - ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal) + ScopedGfxSetting(SetArg (*aGetPrefFunc)(void), void (*aSetPrefFunc)(SetArg), SetArg aVal) : mSetPrefFunc(aSetPrefFunc) { mOldVal = aGetPrefFunc(); aSetPrefFunc(aVal); } - ~ScopedGfxPref() { + ~ScopedGfxSetting() { mSetPrefFunc(mOldVal); } private: - void (*mSetPrefFunc)(T); - T mOldVal; + void (*mSetPrefFunc)(SetArg); + Storage mOldVal; }; #define SCOPED_GFX_PREF(prefBase, prefType, prefValue) \ - ScopedGfxPref<prefType> pref_##prefBase( \ + ScopedGfxSetting<prefType, prefType> pref_##prefBase( \ &(gfxPrefs::prefBase), \ &(gfxPrefs::Set##prefBase), \ prefValue) +#define SCOPED_GFX_VAR(varBase, varType, varValue) \ + ScopedGfxSetting<const varType&, varType> var_##varBase( \ + &(gfxVars::varBase), \ + &(gfxVars::Set##varBase), \ + varValue) + static TimeStamp GetStartupTime() { static TimeStamp sStartupTime = TimeStamp::Now(); return sStartupTime; } class MockContentController : public GeckoContentController { public: MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
--- a/gfx/layers/apz/test/gtest/TestEventRegions.cpp +++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp @@ -156,17 +156,17 @@ protected: layers[3]->SetEventRegions(regions); registration = MakeUnique<ScopedLayerTreeRegistration>(manager, LayersId{0}, root, mcc); manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); } }; TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateEventRegionsLayerTree1(); TestAsyncPanZoomController* root = ApzcOf(layers[0]); TestAsyncPanZoomController* left = ApzcOf(layers[1]); TestAsyncPanZoomController* bottom = ApzcOf(layers[2]); MockFunction<void(std::string checkPointName)> check; @@ -223,17 +223,17 @@ TEST_F(APZEventRegionsTester, HitRegionA // parent layer's hit region. Verify that it comes out of the APZC's // content controller, which indicates the input events got routed correctly // to the APZC. EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, _, _, rootApzc->GetGuid(), _)).Times(1); Tap(manager, ScreenIntPoint(10, 160), TimeDuration::FromMilliseconds(100)); } TEST_F(APZEventRegionsTester, Obscuration) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateObscuringLayerTree(); ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc); manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); TestAsyncPanZoomController* parent = ApzcOf(layers[1]); TestAsyncPanZoomController* child = ApzcOf(layers[2]);
--- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp +++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp @@ -99,17 +99,17 @@ protected: }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID); } }; // A simple hit testing test that doesn't involve any transforms on layers. TEST_F(APZHitTestingTester, HitTesting1) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateHitTesting1LayerTree(); ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc); // No APZC attached so hit testing will return no APZC at (20,20) RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20)); TestAsyncPanZoomController* nullAPZC = nullptr; EXPECT_EQ(nullAPZC, hit.get()); @@ -166,17 +166,17 @@ TEST_F(APZHitTestingTester, HitTesting1) hit = GetTargetAPZC(ScreenPoint(-1000, 10)); EXPECT_EQ(nullAPZC, hit.get()); EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc); EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko); } // A more involved hit testing test that involves css and async transforms. TEST_F(APZHitTestingTester, HitTesting2) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); SCOPED_GFX_PREF(APZVelocityBias, float, 0.0); // Velocity bias can cause extra repaint requests CreateHitTesting2LayerTree(); ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc); manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); // At this point, the following holds (all coordinates in screen pixels): @@ -277,17 +277,17 @@ TEST_F(APZHitTestingTester, HitTesting2) EXPECT_EQ(apzcroot, hit.get()); // transformToApzc doesn't unapply the root's own async transform EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc.TransformPoint(ScreenPoint(25, 25))); // transformToGecko unapplies the full async transform of -100 pixels EXPECT_EQ(ScreenPoint(25, 25), transformToGecko.TransformPoint(ParentLayerPoint(25, 25))); } TEST_F(APZHitTestingTester, HitTesting3) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); const char* layerTreeSyntax = "c(t)"; // LayerID 0 1 nsIntRegion layerVisibleRegions[] = { nsIntRegion(IntRect(0,0,200,200)), nsIntRegion(IntRect(0,0,50,50)) }; Matrix4x4 transforms[] = { @@ -303,17 +303,17 @@ TEST_F(APZHitTestingTester, HitTesting3) manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 75)); EXPECT_EQ(ApzcOf(layers[1]), hit.get()); } TEST_F(APZHitTestingTester, ComplexMultiLayerTree) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateComplexMultiLayerTree(); ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc); manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0); /* The layer tree looks like this: 0
--- a/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp +++ b/gfx/layers/apz/test/gtest/TestScrollHandoff.cpp @@ -235,17 +235,17 @@ TEST_F(APZScrollHandoffTester, LayerStru } // Test that putting a second finger down on an APZC while a down-chain APZC // is overscrolled doesn't result in being stuck in overscroll. TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1073250) { // Enable overscrolling. SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateScrollHandoffLayerTree1(); TestAsyncPanZoomController* child = ApzcOf(layers[1]); // Pan, causing the parent APZC to overscroll. Pan(manager, 10, 40, PanOptions::KeepFingerDown); EXPECT_FALSE(child->IsOverscrolled()); @@ -274,17 +274,17 @@ TEST_F(APZScrollHandoffTester, StuckInOv // This is almost exactly like StuckInOverscroll_Bug1073250, except the // APZC receiving the input events for the first touch block is the child // (and thus not the same APZC that overscrolls, which is the parent). TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1231228) { // Enable overscrolling. SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateScrollHandoffLayerTree1(); TestAsyncPanZoomController* child = ApzcOf(layers[1]); // Pan, causing the parent APZC to overscroll. Pan(manager, 60, 90, PanOptions::KeepFingerDown); EXPECT_FALSE(child->IsOverscrolled()); @@ -342,17 +342,17 @@ TEST_F(APZScrollHandoffTester, StuckInOv // Make sure nothing is overscrolled. EXPECT_FALSE(child->IsOverscrolled()); EXPECT_FALSE(rootApzc->IsOverscrolled()); } TEST_F(APZScrollHandoffTester, StuckInOverscroll_Bug1240202b) { // Enable overscrolling. SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true); - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateScrollHandoffLayerTree1(); TestAsyncPanZoomController* child = ApzcOf(layers[1]); // Pan, causing the parent APZC to overscroll. Pan(manager, 60, 90, PanOptions::KeepFingerDown); EXPECT_FALSE(child->IsOverscrolled()); @@ -406,17 +406,17 @@ TEST_F(APZScrollHandoffTester, OpposingC EXPECT_FALSE(rootApzc->IsOverscrolled()); } // Test that flinging in a direction where one component of the fling goes into // overscroll but the other doesn't, results in just the one component being // handed off to the parent, while the original APZC continues flinging in the // other direction. TEST_F(APZScrollHandoffTester, PartialFlingHandoff) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateScrollHandoffLayerTree1(); // Fling up and to the left. The child APZC has room to scroll up, but not // to the left, so the horizontal component of the fling should be handed // off to the parent APZC. Pan(manager, ScreenIntPoint(90, 90), ScreenIntPoint(55, 55)); @@ -494,24 +494,24 @@ TEST_F(APZScrollHandoffTester, Scrollgra // Check that it is the scrollgrab parent that's in a fling, not the child. rootApzc->AssertStateIsFling(); childApzc->AssertStateIsReset(); } TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration1) { SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateScrollgrabLayerTree(true /* make parent scrollable */); TestFlingAcceleration(); } TEST_F(APZScrollHandoffTester, ScrollgrabFlingAcceleration2) { SCOPED_GFX_PREF(APZFlingMinVelocityThreshold, float, 0.0f); - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); CreateScrollgrabLayerTree(false /* do not make parent scrollable */); TestFlingAcceleration(); } TEST_F(APZScrollHandoffTester, ImmediateHandoffDisallowed_Pan) { SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false); CreateScrollHandoffLayerTree1();
--- a/gfx/layers/apz/test/gtest/TestSnapping.cpp +++ b/gfx/layers/apz/test/gtest/TestSnapping.cpp @@ -10,17 +10,17 @@ #include "InputUtils.h" class APZCSnappingTester : public APZCTreeManagerTester { }; TEST_F(APZCSnappingTester, Bug1265510) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); const char* layerTreeSyntax = "c(t)"; nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0, 0, 100, 100)), nsIntRegion(IntRect(0, 100, 100, 100)) }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 200)); @@ -63,17 +63,17 @@ TEST_F(APZCSnappingTester, Bug1265510) // However, the outer frame should also continue to the snap point, otherwise // it is demonstrating incorrect behaviour by violating the mandatory snapping. outer->AdvanceAnimationsUntilEnd(); EXPECT_EQ(100.0f, outer->GetCurrentAsyncScrollOffset(AsyncPanZoomController::AsyncTransformConsumer::eForHitTesting).y); } TEST_F(APZCSnappingTester, Snap_After_Pinch) { - SCOPED_GFX_PREF(WebRenderHitTest, bool, false); + SCOPED_GFX_VAR(UseWebRender, bool, false); const char* layerTreeSyntax = "c"; nsIntRegion layerVisibleRegion[] = { nsIntRegion(IntRect(0, 0, 100, 100)), }; root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers); SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 200));
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp +++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp @@ -236,17 +236,16 @@ struct Grouper // Returns whether this is an item for which complete invalidation was // reliant on LayerTreeInvalidation in the pre-webrender world. static bool IsContainerLayerItem(nsDisplayItem* aItem) { switch (aItem->GetType()) { case DisplayItemType::TYPE_TRANSFORM: - case DisplayItemType::TYPE_LAYER_EVENT_REGIONS: case DisplayItemType::TYPE_OPACITY: case DisplayItemType::TYPE_FILTER: case DisplayItemType::TYPE_BLEND_CONTAINER: case DisplayItemType::TYPE_BLEND_MODE: case DisplayItemType::TYPE_MASK: { return true; } default: { @@ -1226,33 +1225,22 @@ WebRenderCommandBuilder::CreateWebRender GP("actually entering the grouping code\n"); DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder, aSc, aBuilder, aResources); return; } mClipManager.BeginList(aSc); bool apzEnabled = mManager->AsyncPanZoomEnabled(); - EventRegions eventRegions; FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList); while (nsDisplayItem* i = iter.GetNext()) { nsDisplayItem* item = i; DisplayItemType itemType = item->GetType(); - // If the item is a event regions item, but is empty (has no regions in it) - // then we should just throw it out - if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { - nsDisplayLayerEventRegions* eventRegions = - static_cast<nsDisplayLayerEventRegions*>(item); - if (eventRegions->IsEmpty()) { - continue; - } - } - // Peek ahead to the next item and try merging with it or swapping with it // if necessary. AutoTArray<nsDisplayItem*, 1> mergedItems; mergedItems.AppendElement(item); while (nsDisplayItem* peek = iter.PeekNext()) { if (!item->CanMerge(peek)) { break; } @@ -1281,66 +1269,27 @@ WebRenderCommandBuilder::CreateWebRender // display item than previously, so we can't squash the display items // into the same "layer". const ActiveScrolledRoot* asr = item->GetActiveScrolledRoot(); if (asr != mLastAsr) { mLastAsr = asr; forceNewLayerData = true; } - // If we're creating a new layer data then flush whatever event regions - // we've collected onto the old layer. - if (forceNewLayerData && !eventRegions.IsEmpty()) { - // If eventRegions is non-empty then we must have a layer data already, - // because we (below) force one if we encounter an event regions item - // with an empty layer data list. Additionally, the most recently - // created layer data must have been created from an item whose ASR - // is the same as the ASR on the event region items that were collapsed - // into |eventRegions|. This is because any ASR change causes us to force - // a new layer data which flushes the eventRegions. - MOZ_ASSERT(!mLayerScrollData.empty()); - mLayerScrollData.back().AddEventRegions(eventRegions); - eventRegions.SetEmpty(); - } - - // Collapse event region data into |eventRegions|, which will either be - // empty, or filled with stuff from previous display items with the same - // ASR. - if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { - nsDisplayLayerEventRegions* regionsItem = - static_cast<nsDisplayLayerEventRegions*>(item); - int32_t auPerDevPixel = item->Frame()->PresContext()->AppUnitsPerDevPixel(); - EventRegions regions( - regionsItem->HitRegion().ScaleToOutsidePixels(1.0f, 1.0f, auPerDevPixel), - regionsItem->MaybeHitRegion().ScaleToOutsidePixels(1.0f, 1.0f, auPerDevPixel), - regionsItem->DispatchToContentHitRegion().ScaleToOutsidePixels(1.0f, 1.0f, auPerDevPixel), - regionsItem->NoActionRegion().ScaleToOutsidePixels(1.0f, 1.0f, auPerDevPixel), - regionsItem->HorizontalPanRegion().ScaleToOutsidePixels(1.0f, 1.0f, auPerDevPixel), - regionsItem->VerticalPanRegion().ScaleToOutsidePixels(1.0f, 1.0f, auPerDevPixel), - /* mDTCRequiresTargetConfirmation = */ false); - - eventRegions.OrWith(regions); - if (mLayerScrollData.empty()) { - // If we don't have a layer data yet then create one because we will - // need it to store this event region information. - forceNewLayerData = true; - } - } - // If we're going to create a new layer data for this item, stash the // ASR so that if we recurse into a sublist they will know where to stop // walking up their ASR chain when building scroll metadata. if (forceNewLayerData) { mAsrStack.push_back(asr); } } mClipManager.BeginItem(item, aSc); - if (itemType != DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { + { // scope restoreDoGrouping AutoRestore<bool> restoreDoGrouping(mDoGrouping); if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) { // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper // display items (like animated transforms / opacity) share the same // animated geometry root, so we can combine subsequent items of that // type into the same image. mDoGrouping = true; GP("attempting to enter the grouping code\n"); @@ -1412,41 +1361,20 @@ WebRenderCommandBuilder::CreateWebRender } else { // This is the "simple" case where we don't need to create two // WebRenderLayerScrollData items; we can just create one that also // holds the deferred transform matrix, if any. mLayerScrollData.emplace_back(); mLayerScrollData.back().Initialize(mManager->GetScrollData(), item, descendants, stopAtAsr, deferred ? Some((*deferred)->GetTransform().GetMatrix()) : Nothing()); } - } else if (mLayerScrollData.size() != layerCountBeforeRecursing && - !eventRegions.IsEmpty()) { - // We are not forcing a new layer for |item|, but we did create some - // layers while recursing. In this case, we need to make sure any - // event regions that we were carrying end up on the right layer. So we - // do an event region "flush" but retroactively; i.e. the event regions - // end up on the layer that was mLayerScrollData.back() prior to the - // recursion. - MOZ_ASSERT(layerCountBeforeRecursing > 0); - mLayerScrollData[layerCountBeforeRecursing - 1].AddEventRegions(eventRegions); - eventRegions.SetEmpty(); } } } - // If we have any event region info left over we need to flush it before we - // return. Again, at this point the layer data list must be non-empty, and - // the most recently created layer data will have been created by an item - // with matching ASRs. - if (!eventRegions.IsEmpty()) { - MOZ_ASSERT(apzEnabled); - MOZ_ASSERT(!mLayerScrollData.empty()); - mLayerScrollData.back().AddEventRegions(eventRegions); - } - mClipManager.EndList(aSc); } void WebRenderCommandBuilder::PushOverrideForASR(const ActiveScrolledRoot* aASR, const Maybe<wr::WrClipId>& aClipId) { mClipManager.PushOverrideForASR(aASR, aClipId);
--- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -512,17 +512,16 @@ private: DECL_GFX_PREF(Once, "gfx.webrender.all", WebRenderAll, bool, false); DECL_GFX_PREF(Once, "gfx.webrender.all.qualified", WebRenderAllQualified, bool, false); DECL_GFX_PREF(Once, "gfx.webrender.async-scene-build", WebRenderAsyncSceneBuild, bool, true); DECL_GFX_PREF(Once, "gfx.webrender.enabled", WebRenderEnabledDoNotUseDirectly, bool, false); DECL_GFX_PREF(Live, "gfx.webrender.blob-images", WebRenderBlobImages, bool, true); DECL_GFX_PREF(Live, "gfx.webrender.blob.invalidation", WebRenderBlobInvalidation, bool, false); DECL_GFX_PREF(Live, "gfx.webrender.highlight-painted-layers",WebRenderHighlightPaintedLayers, bool, false); - DECL_GFX_PREF(Live, "gfx.webrender.hit-test", WebRenderHitTest, bool, true); // Use vsync events generated by hardware DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs", WorkAroundDriverBugs, bool, true); DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist", IgnoreDXInterop2Blacklist, bool, false); DECL_GFX_PREF(Live, "gl.msaa-level", MSAALevel, uint32_t, 2); #if defined(XP_MACOSX) DECL_GFX_PREF(Live, "gl.multithreaded", GLMultithreaded, bool, false); @@ -675,21 +674,18 @@ private: DECL_GFX_PREF(Live, "layout.display-list.retain.verify", LayoutVerifyRetainDisplayList, bool, false); DECL_GFX_PREF(Live, "layout.display-list.retain.verify.order", LayoutVerifyRetainDisplayListOrder, bool, false); DECL_GFX_PREF(Live, "layout.display-list.rebuild-frame-limit", LayoutRebuildFrameLimit, uint32_t, 500); DECL_GFX_PREF(Live, "layout.display-list.dump", LayoutDumpDisplayList, bool, false); DECL_GFX_PREF(Live, "layout.display-list.dump-content", LayoutDumpDisplayListContent, bool, false); DECL_GFX_PREF(Live, "layout.display-list.dump-parent", LayoutDumpDisplayListParent, bool, false); DECL_GFX_PREF(Live, "layout.display-list.show-rebuild-area", LayoutDisplayListShowArea, bool, false); - DECL_GFX_PREF(Once, "layout.simple-event-region-items", SimpleEventRegionItems, bool, true); + DECL_GFX_PREF(Once, "layout.frame_rate", LayoutFrameRate, int32_t, -1); DECL_GFX_PREF(Once, "layout.less-event-region-items", LessEventRegionItems, bool, true); - - DECL_GFX_PREF(Live, "layout.event-regions.enabled", LayoutEventRegionsEnabledDoNotUseDirectly, bool, false); - DECL_GFX_PREF(Once, "layout.frame_rate", LayoutFrameRate, int32_t, -1); DECL_GFX_PREF(Live, "layout.min-active-layer-size", LayoutMinActiveLayerSize, int, 64); DECL_GFX_PREF(Once, "layout.paint_rects_separately", LayoutPaintRectsSeparately, bool, true); // This and code dependent on it should be removed once containerless scrolling looks stable. DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers", LayoutUseContainersForRootFrames, bool, true); // This pref is to be set by test code only. DECL_GFX_PREF(Live, "layout.scrollbars.always-layerize-track", AlwaysLayerizeScrollbarTrackTestOnly, bool, false); DECL_GFX_PREF(Live, "layout.smaller-painted-layers", LayoutSmallerPaintedLayers, bool, false);
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3673,17 +3673,17 @@ nsLayoutUtils::PaintFrame(gfxContext* aR } } nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id); builder.SetVisibleRect(visibleRect); builder.SetIsBuilding(true); builder.SetAncestorHasApzAwareEventHandler( - nsDisplayListBuilder::LayerEventRegionsEnabled() && + gfxPlatform::AsyncPanZoomEnabled() && nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell)); DisplayListChecker beforeMergeChecker; DisplayListChecker toBeMergedChecker; DisplayListChecker afterMergeChecker; // Attempt to do a partial build and merge into the existing list. // This calls BuildDisplayListForStacking context on a subset of the
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -2816,26 +2816,21 @@ nsIFrame::BuildDisplayListForStackingCon const nsStyleDisplay* disp = StyleDisplay(); const nsStyleEffects* effects = StyleEffects(); EffectSet* effectSet = EffectSet::GetEffectSet(this); // We can stop right away if this is a zero-opacity stacking context and // we're painting, and we're not animating opacity. Don't do this // if we're going to compute plugin geometry, since opacity-0 plugins // need to have display items built for them. - bool needEventRegions = - aBuilder->IsBuildingLayerEventRegions() && - StyleUserInterface()->GetEffectivePointerEvents(this) != - NS_STYLE_POINTER_EVENTS_NONE; bool opacityItemForEventsAndPluginsOnly = false; if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() && !(disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY) && !nsLayoutUtils::HasAnimationOfProperty(effectSet, eCSSProperty_opacity)) { - if (needEventRegions || - aBuilder->WillComputePluginGeometry()) { + if (aBuilder->WillComputePluginGeometry()) { opacityItemForEventsAndPluginsOnly = true; } else { return; } } if (disp->mWillChangeBitField != 0) { aBuilder->AddToWillChangeBudget(this, GetSize()); @@ -3064,23 +3059,16 @@ nsIFrame::BuildDisplayListForStackingCon if (extend3DContext) { // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are // going to be forced to descend into frames. aBuilder->MarkPreserve3DFramesForDisplayList(this); } aBuilder->AdjustWindowDraggingRegion(this); - nsDisplayLayerEventRegions* eventRegions = nullptr; - if (aBuilder->IsBuildingLayerEventRegions()) { - eventRegions = MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, this); - eventRegions->AddFrame(aBuilder, this); - aBuilder->SetLayerEventRegions(eventRegions); - } - aBuilder->BuildCompositorHitTestInfoIfNeeded(this, set.BorderBackground(), true); MarkAbsoluteFramesForDisplayList(aBuilder); aBuilder->Check(); BuildDisplayList(aBuilder, set); aBuilder->Check(); @@ -3105,28 +3093,16 @@ nsIFrame::BuildDisplayListForStackingCon if (aBuilder->ContainsBlendMode() && aBuilder->IsRetainingDisplayList()) { if (!aBuilder->GetDirtyRect().Contains(aBuilder->GetVisibleRect())) { aBuilder->SetPartialBuildFailed(true); } else { aBuilder->SetDisablePartialUpdates(true); } } - - if (eventRegions) { - // If the event regions item ended up empty, throw it away rather than - // adding it to the display list. - if (!eventRegions->IsEmpty()) { - set.BorderBackground()->AppendToBottom(eventRegions); - } else { - aBuilder->SetLayerEventRegions(nullptr); - eventRegions->Destroy(aBuilder); - eventRegions = nullptr; - } - } } if (aBuilder->IsBackgroundOnly()) { set.BlockBorderBackgrounds()->DeleteAll(aBuilder); set.Floats()->DeleteAll(aBuilder); set.Content()->DeleteAll(aBuilder); set.PositionedDescendants()->DeleteAll(aBuilder); set.Outlines()->DeleteAll(aBuilder); @@ -3530,18 +3506,17 @@ nsIFrame::BuildDisplayListForChild(nsDis nsIFrame* child = aChild; if (child->HasAnyStateBits( NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY)) return; aBuilder->ClearWillChangeBudget(child); const bool shortcutPossible = aBuilder->IsPaintingToWindow() && - (aBuilder->IsBuildingLayerEventRegions() || - aBuilder->BuildCompositorHitTestInfo()); + aBuilder->BuildCompositorHitTestInfo(); const bool doingShortcut = shortcutPossible && (child->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST) && // Animations may change the value of |HasOpacity()|. !(child->GetContent() && child->GetContent()->MayHaveAnimations()); // dirty rect in child-relative coordinates @@ -3566,21 +3541,16 @@ nsIFrame::BuildDisplayListForChild(nsDis buildingForChild(aBuilder, child, visible, dirty, false); CheckForApzAwareEventHandlers(aBuilder, child); aBuilder->BuildCompositorHitTestInfoIfNeeded(child, aLists.BorderBackground(), false); - nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); - if (eventRegions) { - eventRegions->AddFrame(aBuilder, child); - } - child->MarkAbsoluteFramesForDisplayList(aBuilder); aBuilder->AdjustWindowDraggingRegion(child); aBuilder->Check(); child->BuildDisplayList(aBuilder, aLists); aBuilder->Check(); aBuilder->DisplayCaret(child, aLists.Content()); #ifdef DEBUG DisplayDebugBorders(aBuilder, child, aLists); @@ -3780,40 +3750,16 @@ nsIFrame::BuildDisplayListForChild(nsDis aBuilder->IntersectDirtyRect(*clipPropClip); clipState.ClipContentDescendants( *clipPropClip + aBuilder->ToReferenceFrame(child)); awayFromCommonPath = true; } child->MarkAbsoluteFramesForDisplayList(aBuilder); - if (aBuilder->IsBuildingLayerEventRegions()) { - // If this frame has a different animated geometry root than its parent, - // make sure we accumulate event regions for its layer. - if (buildingForChild.IsAnimatedGeometryRoot() || isPositioned) { - nsDisplayLayerEventRegions* eventRegions = - MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, child); - eventRegions->AddFrame(aBuilder, child); - aBuilder->SetLayerEventRegions(eventRegions); - - if (isPositioned) { - // We need this nsDisplayLayerEventRegions to be sorted with the positioned - // elements as positioned elements will be sorted on top of normal elements - list.AppendToTop(eventRegions); - } else { - aLists.BorderBackground()->AppendToTop(eventRegions); - } - } else { - nsDisplayLayerEventRegions* eventRegions = aBuilder->GetLayerEventRegions(); - if (eventRegions) { - eventRegions->AddFrame(aBuilder, child); - } - } - } - const bool differentAGR = buildingForChild.IsAnimatedGeometryRoot() || isPositioned; if (!awayFromCommonPath && shortcutPossible && !differentAGR && !buildingForChild.MaybeAnimatedGeometryRoot()) { // The shortcut is available for the child for next time. child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST); }
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -3716,22 +3716,16 @@ ScrollFrameHelper::BuildDisplayList(nsDi scrollbarStyles.mOverscrollBehaviorY != StyleOverscrollBehavior::Auto) { info |= CompositorHitTestInfo::eRequiresTargetConfirmation; } nsDisplayCompositorHitTestInfo* hitInfo = MakeDisplayItem<nsDisplayCompositorHitTestInfo>(aBuilder, mScrolledFrame, info, 1, Some(mScrollPort + aBuilder->ToReferenceFrame(mOuter))); AppendInternalItemToTop(scrolledContent, hitInfo, Some(INT32_MAX)); } - if (aBuilder->IsBuildingLayerEventRegions()) { - nsDisplayLayerEventRegions* inactiveRegionItem = - MakeDisplayItem<nsDisplayLayerEventRegions>(aBuilder, mScrolledFrame, 1); - inactiveRegionItem->AddInactiveScrollPort(mScrolledFrame, mScrollPort + aBuilder->ToReferenceFrame(mOuter)); - AppendInternalItemToTop(scrolledContent, inactiveRegionItem, Some(INT32_MAX)); - } } if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) { aBuilder->AppendNewScrollInfoItemForHoisting( MakeDisplayItem<nsDisplayScrollInfoLayer>(aBuilder, mScrolledFrame, mOuter)); } }
--- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -492,17 +492,17 @@ nsSubDocumentFrame::BuildDisplayList(nsD nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter( aBuilder, ignoreViewportScrolling && rootScrollFrame && rootScrollFrame->GetContent() ? nsLayoutUtils::FindOrCreateIDFor(rootScrollFrame->GetContent()) : aBuilder->GetCurrentScrollParentId()); bool hasDocumentLevelListenersForApzAwareEvents = - nsDisplayListBuilder::LayerEventRegionsEnabled() && + gfxPlatform::AsyncPanZoomEnabled() && nsLayoutUtils::HasDocumentLevelListenersForApzAwareEvents(presShell); aBuilder->SetAncestorHasApzAwareEventHandler(hasDocumentLevelListenersForApzAwareEvents); subdocRootFrame-> BuildDisplayListForStackingContext(aBuilder, &childItems); } if (!aBuilder->IsForEventDelivery()) {
--- a/layout/painting/FrameLayerBuilder.cpp +++ b/layout/painting/FrameLayerBuilder.cpp @@ -583,24 +583,17 @@ public: AnimatedGeometryRoot* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; } /** * A region including the horizontal pan, vertical pan, and no action regions. */ nsRegion CombinedTouchActionRegion(); /** - * Add the given hit regions to the hit regions for this PaintedLayer. - */ - void AccumulateEventRegions(ContainerState* aState, - nsDisplayLayerEventRegions* aEventRegions); - - /** - * Similar to AccumulateEventRegions() but uses different display item to - * track hit regions. + * Add the given hit test info to the hit regions for this PaintedLayer. */ void AccumulateHitTestInfo(ContainerState* aState, nsDisplayCompositorHitTestInfo* aItem); /** * If this represents only a nsDisplayImage, and the image type supports being * optimized to an ImageLayer, returns true. */ @@ -3399,17 +3392,16 @@ void ContainerState::FinishPaintedLayerD if (!layer) { // We couldn't optimize to an image layer or a color layer above. layer = data->mLayer; layer->SetClipRect(Nothing()); FLB_LOG_PAINTED_LAYER_DECISION(data, " Selected painted layer=%p\n", layer.get()); } for (auto& item : data->mAssignedDisplayItems) { - MOZ_ASSERT(item.mItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS); MOZ_ASSERT(item.mItem->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO); if (item.mType == DisplayItemEntryType::POP_OPACITY) { // Do not invalidate for end markers. continue; } InvalidateForLayerChange(item.mItem, data->mLayer, item.mDisplayItemData); @@ -3819,48 +3811,16 @@ PaintedLayerData::CombinedTouchActionReg { nsRegion result; result.Or(mHorizontalPanRegion, mVerticalPanRegion); result.OrWith(mNoActionRegion); return result; } void -PaintedLayerData::AccumulateEventRegions(ContainerState* aState, nsDisplayLayerEventRegions* aEventRegions) -{ - FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating event regions %p against pld=%p\n", aEventRegions, this); - - mHitRegion.OrWith(aEventRegions->HitRegion()); - mMaybeHitRegion.OrWith(aEventRegions->MaybeHitRegion()); - mDispatchToContentHitRegion.OrWith(aEventRegions->DispatchToContentHitRegion()); - - // See the comment in nsDisplayList::AddFrame, where the touch action regions - // are handled. The same thing applies here. - bool alreadyHadRegions = !mNoActionRegion.IsEmpty() || - !mHorizontalPanRegion.IsEmpty() || - !mVerticalPanRegion.IsEmpty(); - mNoActionRegion.OrWith(aEventRegions->NoActionRegion()); - mHorizontalPanRegion.OrWith(aEventRegions->HorizontalPanRegion()); - mVerticalPanRegion.OrWith(aEventRegions->VerticalPanRegion()); - if (alreadyHadRegions) { - mDispatchToContentHitRegion.OrWith(CombinedTouchActionRegion()); - } - - // Avoid quadratic performance as a result of the region growing to include - // and arbitrarily large number of rects, which can happen on some pages. - mMaybeHitRegion.SimplifyOutward(8); - mDispatchToContentHitRegion.SimplifyOutward(8); - - // Calculate scaled versions of the bounds of mHitRegion and mMaybeHitRegion - // for quick access in FindPaintedLayerFor(). - mScaledHitRegionBounds = aState->ScaleToOutsidePixels(mHitRegion.GetBounds()); - mScaledMaybeHitRegionBounds = aState->ScaleToOutsidePixels(mMaybeHitRegion.GetBounds()); -} - -void PaintedLayerData::AccumulateHitTestInfo(ContainerState* aState, nsDisplayCompositorHitTestInfo* aItem) { FLB_LOG_PAINTED_LAYER_DECISION(this, "Accumulating hit test info %p against pld=%p\n", aItem, this); const mozilla::DisplayItemClip& clip = aItem->GetClip(); const nsRect area = clip.ApplyNonRoundedIntersection(aItem->Area()); @@ -4292,21 +4252,16 @@ ContainerState::ProcessDisplayItems(nsDi { AUTO_PROFILER_LABEL("ContainerState::ProcessDisplayItems", GRAPHICS); nsPoint topLeft(0,0); int32_t maxLayers = gfxPrefs::MaxActiveLayers(); int layerCount = 0; -#ifdef DEBUG - bool hadLayerEventRegions = false; - bool hadCompositorHitTestInfo = false; -#endif - if (!mManager->IsWidgetLayerManager()) { mPaintedLayerDataTree.InitializeForInactiveLayer(mContainerAnimatedGeometryRoot); } AnimatedGeometryRoot* lastAnimatedGeometryRoot = nullptr; nsPoint lastTopLeft; // Tracks the PaintedLayerData that the item will be accumulated in, if it is @@ -4319,45 +4274,25 @@ ContainerState::ProcessDisplayItems(nsDi nsDisplayItem* i = e.mItem; DisplayItemEntryType marker = e.mType; nsDisplayItem* item = i; MOZ_ASSERT(item); DisplayItemType itemType = item->GetType(); - // If the item is a event regions item, but is empty (has no regions in it) - // then we should just throw it out - if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { -#ifdef DEBUG - hadLayerEventRegions = true; -#endif - nsDisplayLayerEventRegions* eventRegions = - static_cast<nsDisplayLayerEventRegions*>(item); - - if (eventRegions->IsEmpty()) { - continue; - } - } - if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { -#ifdef DEBUG - hadCompositorHitTestInfo = true; -#endif nsDisplayCompositorHitTestInfo* hitTestInfo = static_cast<nsDisplayCompositorHitTestInfo*>(item); if (hitTestInfo->Area().IsEmpty()) { continue; } } - // Only allow either LayerEventRegions or CompositorHitTestInfo items. - MOZ_ASSERT(!(hadLayerEventRegions && hadCompositorHitTestInfo)); - if (marker == DisplayItemEntryType::ITEM) { // Peek ahead to the next item and see if it can be merged with the // current item. nsDisplayItem* peek = iter.PeekNext(); if (peek && item->CanMerge(peek)) { // Create a list of consecutive items that can be merged together. AutoTArray<nsDisplayItem*, 2> mergedItems { item }; while ((peek = iter.PeekNext())) { @@ -4384,18 +4319,17 @@ ContainerState::ProcessDisplayItems(nsDi NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item), "items in a container layer should all have the same app units per dev pixel"); if (mBuilder->NeedToForceTransparentSurfaceForItem(item)) { aList->SetNeedsTransparentSurface(); } if (mParameters.mForEventsAndPluginsOnly && !item->GetChildren() && - (itemType != DisplayItemType::TYPE_LAYER_EVENT_REGIONS && - itemType != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO && + (itemType != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO && itemType != DisplayItemType::TYPE_PLUGIN)) { continue; } LayerState layerState = LAYER_NONE; if (marker == DisplayItemEntryType::ITEM) { layerState = item->GetLayerState(mBuilder, mManager, mParameters); @@ -4435,26 +4369,21 @@ ContainerState::ProcessDisplayItems(nsDi lastAnimatedGeometryRoot = animatedGeometryRoot; } const ActiveScrolledRoot* scrollMetadataASR = layerClipChain ? ActiveScrolledRoot::PickDescendant(itemASR, layerClipChain->mASR) : itemASR; bool snap; nsRect itemContent = item->GetBounds(mBuilder, &snap); - if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { - nsDisplayLayerEventRegions* eventRegions = - static_cast<nsDisplayLayerEventRegions*>(item); - itemContent = eventRegions->GetHitRegionBounds(mBuilder, &snap); - } if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { - nsDisplayCompositorHitTestInfo* eventRegions = + nsDisplayCompositorHitTestInfo* hitInfo = static_cast<nsDisplayCompositorHitTestInfo*>(item); - itemContent = eventRegions->Area(); + itemContent = hitInfo->Area(); } nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap); bool prerenderedTransform = itemType == DisplayItemType::TYPE_TRANSFORM && static_cast<nsDisplayTransform*>(item)->MayBeAnimated(mBuilder); ParentLayerIntRect clipRect; const DisplayItemClip& itemClip = item->GetClip(); if (itemClip.HasClip()) { @@ -4463,18 +4392,17 @@ ContainerState::ProcessDisplayItems(nsDi if (!prerenderedTransform && !IsScrollThumbLayer(item)) { itemDrawRect.IntersectRect(itemDrawRect, clipRect.ToUnknownRect()); } clipRect.MoveBy(ViewAs<ParentLayerPixel>(mParameters.mOffset)); } #ifdef DEBUG nsRect bounds = itemContent; - if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS || - itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { + if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { bounds.SetEmpty(); } if (!bounds.IsEmpty()) { if (itemASR != mContainerASR) { if (Maybe<nsRect> clip = item->GetClipWithRespectToASR(mBuilder, mContainerASR)) { bounds = clip.ref(); } @@ -4839,21 +4767,17 @@ ContainerState::ProcessDisplayItems(nsDi [&](PaintedLayerData* aData) { NewPaintedLayerData(aData, animatedGeometryRoot, itemASR, layerClipChain, scrollMetadataASR, topLeft, referenceFrame, backfaceHidden); }); } MOZ_ASSERT(paintedLayerData); - if (itemType == DisplayItemType::TYPE_LAYER_EVENT_REGIONS) { - nsDisplayLayerEventRegions* eventRegions = - static_cast<nsDisplayLayerEventRegions*>(item); - paintedLayerData->AccumulateEventRegions(this, eventRegions); - } else if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { + if (itemType == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { nsDisplayCompositorHitTestInfo* hitTestInfo = static_cast<nsDisplayCompositorHitTestInfo*>(item); paintedLayerData->AccumulateHitTestInfo(this, hitTestInfo); } else { paintedLayerData->Accumulate(this, item, itemVisibleRect, itemContent, itemClip, layerState, aList, marker); if (!paintedLayerData->mLayer) {
--- a/layout/painting/nsDisplayItemTypesList.h +++ b/layout/painting/nsDisplayItemTypesList.h @@ -27,17 +27,16 @@ DECLARE_DISPLAY_ITEM_TYPE(CANVAS_FOCUS, DECLARE_DISPLAY_ITEM_TYPE(CARET, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(CHECKED_CHECKBOX, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(CHECKED_RADIOBUTTON, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(CLEAR_BACKGROUND, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(COLUMN_RULE, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(COMBOBOX_FOCUS, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(COMPOSITOR_HITTEST_INFO, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(EVENT_RECEIVER, TYPE_RENDERS_NO_IMAGES) -DECLARE_DISPLAY_ITEM_TYPE(LAYER_EVENT_REGIONS, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(FIELDSET_BORDER_BACKGROUND, 0) DECLARE_DISPLAY_ITEM_TYPE(FIXED_POSITION, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(STICKY_POSITION, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BORDER, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BLANK, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(HEADER_FOOTER, TYPE_RENDERS_NO_IMAGES) DECLARE_DISPLAY_ITEM_TYPE(IMAGE, 0) DECLARE_DISPLAY_ITEM_TYPE(LIST_FOCUS, TYPE_RENDERS_NO_IMAGES)
--- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -976,17 +976,16 @@ nsDisplayListBuilder::AutoCurrentActiveS mUsed = true; } nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, nsDisplayListBuilderMode aMode, bool aBuildCaret, bool aRetainingDisplayList) : mReferenceFrame(aReferenceFrame), mIgnoreScrollFrame(nullptr), - mLayerEventRegions(nullptr), mCompositorHitTestInfo(nullptr), mCurrentTableItem(nullptr), mCurrentActiveScrolledRoot(nullptr), mCurrentContainerASR(nullptr), mCurrentFrame(aReferenceFrame), mCurrentReferenceFrame(aReferenceFrame), mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(aReferenceFrame, nullptr, true, aRetainingDisplayList)), mCurrentAGR(mRootAGR), @@ -1030,21 +1029,17 @@ nsDisplayListBuilder::nsDisplayListBuild mHitTestIsForVisibility(false), mIsBuilding(false), mInInvalidSubtree(false), mDisablePartialUpdates(false), mPartialBuildFailed(false) { MOZ_COUNT_CTOR(nsDisplayListBuilder); - const bool useWRHitTest = - gfxPrefs::WebRenderHitTest() && gfxVars::UseWebRender(); - - mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting() && - (useWRHitTest || gfxPrefs::SimpleEventRegionItems()); + mBuildCompositorHitTestInfo = mAsyncPanZoomEnabled && IsForPainting(); mLessEventRegionItems = gfxPrefs::LessEventRegionItems(); nsPresContext* pc = aReferenceFrame->PresContext(); nsIPresShell *shell = pc->PresShell(); if (pc->IsRenderingOnlySelection()) { nsCOMPtr<nsISelectionController> selcon(do_QueryInterface(shell)); if (selcon) { @@ -1075,17 +1070,16 @@ nsDisplayListBuilder::EndFrame() { NS_ASSERTION(!mInInvalidSubtree, "Someone forgot to cleanup mInInvalidSubtree!"); mFrameToAnimatedGeometryRootMap.Clear(); mActiveScrolledRoots.Clear(); FreeClipChains(); FreeTemporaryItems(); nsCSSRendering::EndFrameTreesLocked(); - MOZ_ASSERT(!mLayerEventRegions); MOZ_ASSERT(!mCompositorHitTestInfo); } void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) { mFramesMarkedForDisplay.AppendElement(aFrame); for (nsIFrame* f = aFrame; f; @@ -1374,17 +1368,16 @@ nsDisplayListBuilder::EnterPresShell(nsI } // A non-blank paint is a paint that does not just contain the canvas background. static bool DisplayListIsNonBlank(nsDisplayList* aList) { for (nsDisplayItem* i = aList->GetBottom(); i != nullptr; i = i->GetAbove()) { switch (i->GetType()) { - case DisplayItemType::TYPE_LAYER_EVENT_REGIONS: case DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO: case DisplayItemType::TYPE_CANVAS_BACKGROUND_COLOR: case DisplayItemType::TYPE_CANVAS_BACKGROUND_IMAGE: continue; case DisplayItemType::TYPE_SOLID_COLOR: case DisplayItemType::TYPE_BACKGROUND: case DisplayItemType::TYPE_BACKGROUND_COLOR: if (i->Frame()->IsCanvasFrame()) { @@ -2290,45 +2283,16 @@ nsDisplayListBuilder::ShouldBuildComposi // Hit test flags are different. return true; } // Create a new item if the parent does not contain the child completely. return !mCompositorHitTestInfo->Area().Contains(GetFrameArea(this, aFrame)); } -bool -nsDisplayListBuilder::IsBuildingLayerEventRegions() -{ - if (mBuildCompositorHitTestInfo) { - // If we have webrender hit-testing enabled, then we will build the - // nsDisplayCompositorHitTestInfo items and use those instead of event - // regions, so we don't need to build the event regions. - return false; - } - if (IsPaintingToWindow()) { - // Note: this function and LayerEventRegionsEnabled are the only places - // that get to query LayoutEventRegionsEnabled 'directly' - other code - // should call this function. - return gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() || - mAsyncPanZoomEnabled; - } - return false; -} - -/* static */ bool -nsDisplayListBuilder::LayerEventRegionsEnabled() -{ - // Note: this function and IsBuildingLayerEventRegions are the only places - // that get to query LayoutEventRegionsEnabled 'directly' - other code - // should call this function. - return gfxPrefs::LayoutEventRegionsEnabledDoNotUseDirectly() || - gfxPlatform::AsyncPanZoomEnabled(); -} - void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const { aDestination.BorderBackground()->AppendToTop(BorderBackground()); aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds()); aDestination.Floats()->AppendToTop(Floats()); aDestination.Content()->AppendToTop(Content()); aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants()); aDestination.Outlines()->AppendToTop(Outlines()); @@ -2395,17 +2359,16 @@ nsDisplayList::ComputeVisibilityForRoot( } static nsRegion TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder) { bool snap; nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap); if (aBuilder->IsForPluginGeometry() && - aItem->GetType() != DisplayItemType::TYPE_LAYER_EVENT_REGIONS && aItem->GetType() != DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO) { // Treat all leaf chrome items as opaque, unless their frames are opacity:0. // Since opacity:0 frames generate an nsDisplayOpacity, that item will // not be treated as opaque here, so opacity:0 chrome content will be // effectively ignored, as it should be. // We treat leaf chrome items as opaque to ensure that they cover // content plugins, for security reasons. @@ -5152,213 +5115,16 @@ nsDisplayCompositorHitTestInfo::ZIndex() } void nsDisplayCompositorHitTestInfo::SetOverrideZIndex(int32_t aZIndex) { mOverrideZIndex = Some(aZIndex); } -void -nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame) -{ - NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame), - "Reference frame mismatch"); - CompositorHitTestInfo hitInfo = - aFrame->GetCompositorHitTestInfo(aBuilder); - if (hitInfo == CompositorHitTestInfo::eInvisibleToHitTest) { - return; - } - - // XXX handle other pointerEvents values for SVG - - // XXX Do something clever here for the common case where the border box - // is obviously entirely inside mHitRegion. - nsRect borderBox = GetFrameArea(aBuilder, aFrame); - - if (aFrame != mFrame && - aBuilder->IsRetainingDisplayList()) { - aFrame->AddDisplayItem(this); - } - - bool borderBoxHasRoundedCorners = false; - - // use the NS_FRAME_SIMPLE_EVENT_REGIONS to avoid calling the slightly - // expensive HasNonZeroCorner function if we know from a previous run that - // the frame has zero corners. - bool simpleRegions = aFrame->HasAnyStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS); - if (!simpleRegions) { - if (nsLayoutUtils::HasNonZeroCorner(aFrame->StyleBorder()->mBorderRadius)) { - borderBoxHasRoundedCorners = true; - } else { - aFrame->AddStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS); - } - } - - const DisplayItemClip* clip = DisplayItemClipChain::ClipForASR( - aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder), - aBuilder->CurrentActiveScrolledRoot()); - if (clip) { - borderBox = clip->ApplyNonRoundedIntersection(borderBox); - if (clip->GetRoundedRectCount() > 0) { - borderBoxHasRoundedCorners = true; - } - } - - if (borderBoxHasRoundedCorners || - (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { - mMaybeHitRegion.Add(aFrame, borderBox); - } else { - mHitRegion.Add(aFrame, borderBox); - } - - if (hitInfo & CompositorHitTestInfo::eDispatchToContent) { - mDispatchToContentHitRegion.Add(aFrame, borderBox); - } - - // Touch action region - - auto touchFlags = hitInfo & CompositorHitTestInfo::eTouchActionMask; - if (touchFlags) { - // something was disabled - if (touchFlags == CompositorHitTestInfo::eTouchActionMask) { - // everything was disabled, so touch-action:none - mNoActionRegion.Add(aFrame, borderBox); - } else { - // The event regions code does not store enough information to actually - // represent all the different states. Prior to the introduction of - // CompositorHitTestInfo here in bug 1389149, the following two cases - // were effectively getting collapsed: - // (1) touch-action: auto - // (2) touch-action: manipulation - // In both of these cases, none of {mNoActionRegion, mHorizontalPanRegion, - // mVerticalPanRegion} were modified, and so the fact that case (2) should - // have prevented double-tap-zooming was getting lost. - // With CompositorHitTestInfo we can now represent that case correctly, - // but only if we use CompositorHitTestInfo all the way to the compositor - // (i.e. in the WebRender-enabled case). In the non-WebRender case where - // we still use the event regions, we must collapse these two cases back - // together. Or add another region to the event regions to fix this - // properly. - if (touchFlags == CompositorHitTestInfo::eTouchActionDoubleTapZoomDisabled) { - // the touch-action: manipulation case described above. To preserve the - // existing behaviour, don't touch either mHorizontalPanRegion or - // mVerticalPanRegion - } else { - if (!(hitInfo & CompositorHitTestInfo::eTouchActionPanXDisabled)) { - // pan-x is allowed - mHorizontalPanRegion.Add(aFrame, borderBox); - } - if (!(hitInfo & CompositorHitTestInfo::eTouchActionPanYDisabled)) { - // pan-y is allowed - mVerticalPanRegion.Add(aFrame, borderBox); - } - } - } - } -} - -static void -RemoveFrameFromFrameRects(nsDisplayLayerEventRegions::FrameRects& aFrameRects, nsIFrame* aFrame) -{ - uint32_t i = 0; - uint32_t length = aFrameRects.mFrames.Length(); - while (i < length) { - if (aFrameRects.mFrames[i] == aFrame) { - aFrameRects.mFrames[i] = aFrameRects.mFrames[length - 1]; - aFrameRects.mBoxes[i] = aFrameRects.mBoxes[length - 1]; - length--; - } else { - i++; - } - } - aFrameRects.mFrames.SetLength(length); - aFrameRects.mBoxes.SetLength(length); -} - -void -nsDisplayLayerEventRegions::RemoveFrame(nsIFrame* aFrame) -{ - RemoveFrameFromFrameRects(mHitRegion, aFrame); - RemoveFrameFromFrameRects(mMaybeHitRegion, aFrame); - RemoveFrameFromFrameRects(mDispatchToContentHitRegion, aFrame); - RemoveFrameFromFrameRects(mNoActionRegion, aFrame); - RemoveFrameFromFrameRects(mHorizontalPanRegion, aFrame); - RemoveFrameFromFrameRects(mVerticalPanRegion, aFrame); - - nsDisplayItem::RemoveFrame(aFrame); -} - -void -nsDisplayLayerEventRegions::AddInactiveScrollPort(nsIFrame* aFrame, const nsRect& aRect) -{ - mHitRegion.Add(aFrame, aRect); - mDispatchToContentHitRegion.Add(aFrame, aRect); -} - -bool -nsDisplayLayerEventRegions::IsEmpty() const -{ - // If the hit region and maybe-hit region are empty, then the rest - // must be empty too. - if (mHitRegion.IsEmpty() && mMaybeHitRegion.IsEmpty()) { - MOZ_ASSERT(mDispatchToContentHitRegion.IsEmpty()); - MOZ_ASSERT(mNoActionRegion.IsEmpty()); - MOZ_ASSERT(mHorizontalPanRegion.IsEmpty()); - MOZ_ASSERT(mVerticalPanRegion.IsEmpty()); - return true; - } - return false; -} - -nsRegion -nsDisplayLayerEventRegions::CombinedTouchActionRegion() -{ - nsRegion result; - result.Or(HorizontalPanRegion(), VerticalPanRegion()); - result.OrWith(NoActionRegion()); - return result; -} - -int32_t -nsDisplayLayerEventRegions::ZIndex() const -{ - return mOverrideZIndex ? *mOverrideZIndex : nsDisplayItem::ZIndex(); -} - -void -nsDisplayLayerEventRegions::SetOverrideZIndex(int32_t aZIndex) -{ - mOverrideZIndex = Some(aZIndex); -} - -void -nsDisplayLayerEventRegions::WriteDebugInfo(std::stringstream& aStream) -{ - if (!mHitRegion.IsEmpty()) { - AppendToString(aStream, HitRegion(), " (hitRegion ", ")"); - } - if (!mMaybeHitRegion.IsEmpty()) { - AppendToString(aStream, MaybeHitRegion(), " (maybeHitRegion ", ")"); - } - if (!mDispatchToContentHitRegion.IsEmpty()) { - AppendToString(aStream, DispatchToContentHitRegion(), " (dispatchToContentRegion ", ")"); - } - if (!mNoActionRegion.IsEmpty()) { - AppendToString(aStream, NoActionRegion(), " (noActionRegion ", ")"); - } - if (!mHorizontalPanRegion.IsEmpty()) { - AppendToString(aStream, HorizontalPanRegion(), " (horizPanRegion ", ")"); - } - if (!mVerticalPanRegion.IsEmpty()) { - AppendToString(aStream, VerticalPanRegion(), " (vertPanRegion ", ")"); - } -} - nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder, nsIFrame* aCaretFrame) : nsDisplayItem(aBuilder, aCaretFrame) , mCaret(aBuilder->GetCaret()) , mBounds(aBuilder->GetCaretRect() + ToReferenceFrame()) { MOZ_COUNT_CTOR(nsDisplayCaret); } @@ -6592,18 +6358,17 @@ CollectItemsWithOpacity(nsDisplayList* a // Descend only into wraplists. if (type == DisplayItemType::TYPE_WRAP_LIST && children) { // The current display item has children, process them first. if (!CollectItemsWithOpacity(children, aArray, aMaxChildCount)) { return false; } } - if (type == DisplayItemType::TYPE_LAYER_EVENT_REGIONS || - type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO || + if (type == DisplayItemType::TYPE_COMPOSITOR_HITTEST_INFO || type == DisplayItemType::TYPE_WRAP_LIST) { continue; } if (!i->CanApplyOpacity() || aArray.Length() == aMaxChildCount) { return false; }
--- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -57,17 +57,16 @@ class gfxContext; class nsIContent; class nsDisplayList; class nsDisplayTableItem; class nsIScrollableFrame; class nsSubDocumentFrame; class nsDisplayCompositorHitTestInfo; -class nsDisplayLayerEventRegions; class nsDisplayScrollInfoLayer; class nsCaret; enum class nsDisplayOwnLayerFlags; namespace mozilla { class FrameLayerBuilder; namespace layers { class Layer; @@ -721,22 +720,16 @@ public: /** * Returns true if merging and flattening of display lists should be * performed while computing visibility. */ bool AllowMergingAndFlattening() { return mAllowMergingAndFlattening; } void SetAllowMergingAndFlattening(bool aAllow) { mAllowMergingAndFlattening = aAllow; } - nsDisplayLayerEventRegions* GetLayerEventRegions() { return mLayerEventRegions; } - void SetLayerEventRegions(nsDisplayLayerEventRegions* aItem) - { - mLayerEventRegions = aItem; - } - /** * Sets the current compositor hit test info to |aHitTestInfo|. * This is used during display list building to determine if the parent frame * hit test info contains the same information that child frame needs. */ void SetCompositorHitTestInfo(nsDisplayCompositorHitTestInfo* aHitTestInfo) { mCompositorHitTestInfo = aHitTestInfo; @@ -746,18 +739,16 @@ public: * Builds a new nsDisplayCompositorHitTestInfo for the frame |aFrame| if * needed, and adds it to the top of |aList|. If |aBuildNew| is true, the * previous hit test info will not be reused. */ void BuildCompositorHitTestInfoIfNeeded(nsIFrame* aFrame, nsDisplayList* aList, const bool aBuildNew); - bool IsBuildingLayerEventRegions(); - static bool LayerEventRegionsEnabled(); bool IsInsidePointerEventsNoneDoc() { return CurrentPresShellState()->mInsidePointerEventsNoneDoc; } bool GetAncestorHasApzAwareEventHandler() const { return mAncestorHasApzAwareEventHandler; } void SetAncestorHasApzAwareEventHandler(bool aValue) { @@ -1071,17 +1062,16 @@ public: AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild, const nsRect& aVisibleRect, const nsRect& aDirtyRect, bool aIsRoot) : mBuilder(aBuilder), mPrevFrame(aBuilder->mCurrentFrame), mPrevReferenceFrame(aBuilder->mCurrentReferenceFrame), - mPrevLayerEventRegions(aBuilder->mLayerEventRegions), mPrevCompositorHitTestInfo(aBuilder->mCompositorHitTestInfo), mPrevOffset(aBuilder->mCurrentOffsetToReferenceFrame), mPrevVisibleRect(aBuilder->mVisibleRect), mPrevDirtyRect(aBuilder->mDirtyRect), mPrevAGR(aBuilder->mCurrentAGR), mPrevIsAtRootOfPseudoStackingContext(aBuilder->mIsAtRootOfPseudoStackingContext), mPrevAncestorHasApzAwareEventHandler(aBuilder->mAncestorHasApzAwareEventHandler), mPrevBuildingInvisibleItems(aBuilder->mBuildingInvisibleItems), @@ -1124,33 +1114,31 @@ public: return mCurrentAGRState == AGR_MAYBE; } void RestoreBuildingInvisibleItemsValue() { mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems; } ~AutoBuildingDisplayList() { mBuilder->mCurrentFrame = mPrevFrame; mBuilder->mCurrentReferenceFrame = mPrevReferenceFrame; - mBuilder->mLayerEventRegions = mPrevLayerEventRegions; mBuilder->mCompositorHitTestInfo = mPrevCompositorHitTestInfo; mBuilder->mCurrentOffsetToReferenceFrame = mPrevOffset; mBuilder->mVisibleRect = mPrevVisibleRect; mBuilder->mDirtyRect = mPrevDirtyRect; mBuilder->mCurrentAGR = mPrevAGR; mBuilder->mIsAtRootOfPseudoStackingContext = mPrevIsAtRootOfPseudoStackingContext; mBuilder->mAncestorHasApzAwareEventHandler = mPrevAncestorHasApzAwareEventHandler; mBuilder->mBuildingInvisibleItems = mPrevBuildingInvisibleItems; mBuilder->mInInvalidSubtree = mPrevInInvalidSubtree; } private: nsDisplayListBuilder* mBuilder; AGRState mCurrentAGRState; const nsIFrame* mPrevFrame; const nsIFrame* mPrevReferenceFrame; - nsDisplayLayerEventRegions* mPrevLayerEventRegions; nsDisplayCompositorHitTestInfo* mPrevCompositorHitTestInfo; nsPoint mPrevOffset; nsRect mPrevVisibleRect; nsRect mPrevDirtyRect; RefPtr<AnimatedGeometryRoot> mPrevAGR; bool mPrevIsAtRootOfPseudoStackingContext; bool mPrevAncestorHasApzAwareEventHandler; bool mPrevBuildingInvisibleItems; @@ -1723,18 +1711,16 @@ public: void SetHitTestIsForVisibility(bool aHitTestIsForVisibility) { mHitTestIsForVisibility = aHitTestIsForVisibility; } /** * Represents a region composed of frame/rect pairs. * WeakFrames are used to track whether a rect still belongs to the region. * Modified frames and rects are removed and re-added to the region if needed. - * nsDisplayLayerEventRegions::FrameRects implements the same functionality - * with nsIFrames. */ struct WeakFrameRegion { std::vector<WeakFrame> mFrames; nsTArray<pixman_box32_t> mRects; void Add(nsIFrame* aFrame, const nsRect& aRect) { mFrames.emplace_back(aFrame); @@ -1854,17 +1840,16 @@ private: {} nsIFrame* mFrame; uint32_t mUsage; }; nsIFrame* const mReferenceFrame; nsIFrame* mIgnoreScrollFrame; - nsDisplayLayerEventRegions* mLayerEventRegions; nsDisplayCompositorHitTestInfo* mCompositorHitTestInfo; nsPresArena mPool; RefPtr<mozilla::dom::Selection> mBoundingSelection; AutoTArray<PresShellState,8> mPresShellStates; AutoTArray<nsIFrame*,400> mFramesMarkedForDisplay; AutoTArray<nsIFrame*,40> mFramesMarkedForDisplayIfVisible; @@ -4828,234 +4813,16 @@ private: mozilla::Maybe<mozilla::layers::FrameMetrics::ViewID> mScrollTarget; nsRect mArea; uint32_t mIndex; mozilla::Maybe<int32_t> mOverrideZIndex; int32_t mAppUnitsPerDevPixel; }; /** - * A display item that tracks event-sensitive regions which will be set - * on the ContainerLayer that eventually contains this item. - * - * One of these is created for each stacking context and pseudo-stacking-context. - * It accumulates regions for event targets contributed by the border-boxes of - * frames in its (pseudo) stacking context. A nsDisplayLayerEventRegions - * eventually contributes its regions to the PaintedLayer it is placed in by - * FrameLayerBuilder. (We don't create a display item for every frame that - * could be an event target (i.e. almost all frames), because that would be - * high overhead.) - * - * We always make leaf layers other than PaintedLayers transparent to events. - * For example, an event targeting a canvas or video will actually target the - * background of that element, which is logically in the PaintedLayer behind the - * CanvasFrame or ImageFrame. We only need to create a - * nsDisplayLayerEventRegions when an element's background could be in front - * of a lower z-order element with its own layer. - */ -class nsDisplayLayerEventRegions final : public nsDisplayItem { -public: - nsDisplayLayerEventRegions(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, uint32_t aIndex = 0) - : nsDisplayItem(aBuilder, aFrame) - , mIndex(aIndex) - { - MOZ_COUNT_CTOR(nsDisplayLayerEventRegions); - } - - virtual void Destroy(nsDisplayListBuilder* aBuilder) override - { - if (!aBuilder->IsRetainingDisplayList()) { - nsDisplayItem::Destroy(aBuilder); - return; - } - - RemoveItemFromFrames(mHitRegion); - RemoveItemFromFrames(mMaybeHitRegion); - RemoveItemFromFrames(mDispatchToContentHitRegion); - RemoveItemFromFrames(mNoActionRegion); - RemoveItemFromFrames(mHorizontalPanRegion); - RemoveItemFromFrames(mVerticalPanRegion); - nsDisplayItem::Destroy(aBuilder); - } - - virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, - bool* aSnap) const override - { - *aSnap = false; - return nsRect(); - } - nsRect GetHitRegionBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) - { - *aSnap = false; - // TODO: This constructs the two regions, but we're also doing the same - // work in AccumulateEventRegions. We should avoid doing it twice. - return HitRegion().GetBounds().Union(MaybeHitRegion().GetBounds()); - } - - virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder, - float aOpacity, - const DisplayItemClipChain* aClip) override - { - NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed"); - } - virtual bool CanApplyOpacity() const override - { - return true; - } - - NS_DISPLAY_DECL_NAME("LayerEventRegions", TYPE_LAYER_EVENT_REGIONS) - - // Indicate that aFrame's border-box contributes to the event regions for - // this layer. aFrame must have the same reference frame as mFrame. - void AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); - - // Indicate that an inactive scrollframe's scrollport should be added to the - // dispatch-to-content region, to ensure that APZ lets content create a - // displayport. - void AddInactiveScrollPort(nsIFrame* aFrame, const nsRect& aRect); - - bool IsEmpty() const; - - int32_t ZIndex() const override; - void SetOverrideZIndex(int32_t aZIndex); - - virtual uint32_t GetPerFrameKey() const override - { - return (mIndex << TYPE_BITS) | nsDisplayItem::GetPerFrameKey(); - } - - const nsRegion HitRegion() - { - return nsRegion(mozilla::gfx::ArrayView<pixman_box32_t>(mHitRegion.mBoxes)); - } - const nsRegion MaybeHitRegion() - { - nsRegion result(mozilla::gfx::ArrayView<pixman_box32_t>(mMaybeHitRegion.mBoxes)); - - // Avoid quadratic performance as a result of the region growing to include - // an arbitrarily large number of rects, which can happen on some pages. - // TODO: It would be nice if we could ask the initial construction above - // to include simplification. - result.SimplifyOutward(8); - return result; - } - const nsRegion DispatchToContentHitRegion() - { - nsRegion result(mozilla::gfx::ArrayView<pixman_box32_t>(mDispatchToContentHitRegion.mBoxes)); - - // If this frame has touch-action areas, and there were already - // touch-action areas from some other element on this same event regions, - // then all we know is that there are multiple elements with touch-action - // properties. In particular, we don't know what the relationship is - // between those elements in terms of DOM ancestry, and so we don't know - // how to combine the regions properly. Instead, we just add all the areas - // to the dispatch-to-content region, so that the APZ knows to check with - // the main thread. XXX we need to come up with a better way to do this, - // see bug 1287829. - uint32_t touchActionCount = - mNoActionRegion.mBoxes.Length() + - mHorizontalPanRegion.mBoxes.Length() + - mVerticalPanRegion.mBoxes.Length(); - if (touchActionCount > 1) { - result.OrWith(NoActionRegion()); - result.OrWith(HorizontalPanRegion()); - result.OrWith(VerticalPanRegion()); - } - - result.SimplifyOutward(8); - return result; - } - const nsRegion NoActionRegion() - { - return nsRegion(mozilla::gfx::ArrayView<pixman_box32_t>(mNoActionRegion.mBoxes)); - } - const nsRegion HorizontalPanRegion() - { - return nsRegion(mozilla::gfx::ArrayView<pixman_box32_t>(mHorizontalPanRegion.mBoxes)); - } - const nsRegion VerticalPanRegion() - { - return nsRegion(mozilla::gfx::ArrayView<pixman_box32_t>(mVerticalPanRegion.mBoxes)); - } - nsRegion CombinedTouchActionRegion(); - - virtual void WriteDebugInfo(std::stringstream& aStream) override; - - // TODO: nsTArray (vector) might not be a great data structure - // choice since we need to remove elements from the middle. - // Should profile and try figure out the best approach - // here. - struct FrameRects { - void Add(nsIFrame* aFrame, const nsRect& aRect) { - mBoxes.AppendElement(nsRegion::RectToBox(aRect)); - mFrames.AppendElement(aFrame); - } - void Add(nsIFrame* aFrame, const pixman_box32& aBox) { - mBoxes.AppendElement(aBox); - mFrames.AppendElement(aFrame); - } - void Add(const FrameRects& aOther) { - mBoxes.AppendElements(aOther.mBoxes); - mFrames.AppendElements(aOther.mFrames); - } - - bool IsEmpty() const { - return mBoxes.IsEmpty(); - } - - nsTArray<pixman_box32_t> mBoxes; - nsTArray<nsIFrame*> mFrames; - }; - - virtual void RemoveFrame(nsIFrame* aFrame) override; - -private: - virtual ~nsDisplayLayerEventRegions() - { - MOZ_COUNT_DTOR(nsDisplayLayerEventRegions); - } - - void RemoveItemFromFrames(FrameRects& aFrameRects) - { - for (nsIFrame* f : aFrameRects.mFrames) { - if (f != mFrame) { - f->RemoveDisplayItem(this); - } - } - } - - friend bool MergeLayerEventRegions(nsDisplayItem*, nsDisplayItem*); - - // Relative to aFrame's reference frame. - // These are the points that are definitely in the hit region. - FrameRects mHitRegion; - // These are points that may or may not be in the hit region. Only main-thread - // event handling can tell for sure (e.g. because complex shapes are present). - FrameRects mMaybeHitRegion; - // These are points that need to be dispatched to the content thread for - // resolution. Always contained in the union of mHitRegion and mMaybeHitRegion. - FrameRects mDispatchToContentHitRegion; - // These are points where panning is disabled, as determined by the touch-action - // property. Always contained in the union of mHitRegion and mMaybeHitRegion. - FrameRects mNoActionRegion; - // These are points where panning is horizontal, as determined by the touch-action - // property. Always contained in the union of mHitRegion and mMaybeHitRegion. - FrameRects mHorizontalPanRegion; - // These are points where panning is vertical, as determined by the touch-action - // property. Always contained in the union of mHitRegion and mMaybeHitRegion. - FrameRects mVerticalPanRegion; - // If these event regions are for an inactive scroll frame, the z-index of - // this display item is overridden to be the largest z-index of the content - // in the scroll frame. This ensures that the event regions item remains on - // top of the content after sorting items by z-index. - mozilla::Maybe<int32_t> mOverrideZIndex; - uint32_t mIndex; -}; - -/** * A class that lets you wrap a display list as a display item. * * GetUnderlyingFrame() is troublesome for wrapped lists because if the wrapped * list has many items, it's not clear which one has the 'underlying frame'. * Thus we force the creator to specify what the underlying frame is. The * underlying frame should be the root of a stacking context, because sorting * a list containing this item will not get at the children. *
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -600,19 +600,16 @@ pref("media.cubeb.sandbox", false); pref("media.webaudio.audiocontextoptions-samplerate.enabled", true); // Weather we allow AMD switchable graphics pref("layers.amd-switchable-gfx.enabled", true); // Whether to use async panning and zooming pref("layers.async-pan-zoom.enabled", true); -// Whether to enable event region building during painting -pref("layout.event-regions.enabled", false); - // Whether to enable arbitrary layer geometry for OpenGL compositor pref("layers.geometry.opengl.enabled", true); // Whether to enable arbitrary layer geometry for Basic compositor pref("layers.geometry.basic.enabled", true); // Whether to enable arbitrary layer geometry for DirectX compositor pref("layers.geometry.d3d11.enabled", true); @@ -868,17 +865,16 @@ pref("gfx.webrender.program-binary-disk" #ifdef XP_MACOSX pref("gfx.compositor.glcontext.opaque", false); #endif pref("gfx.webrender.highlight-painted-layers", false); pref("gfx.webrender.async-scene-build", true); pref("gfx.webrender.blob-images", true); pref("gfx.webrender.blob.invalidation", true); -pref("gfx.webrender.hit-test", true); // WebRender debugging utilities. pref("gfx.webrender.debug.texture-cache", false); pref("gfx.webrender.debug.render-targets", false); pref("gfx.webrender.debug.alpha-primitives", false); pref("gfx.webrender.debug.profiler", false); pref("gfx.webrender.debug.gpu-time-queries", false); pref("gfx.webrender.debug.gpu-sample-queries", false);
--- a/taskcluster/ci/config.yml +++ b/taskcluster/ci/config.yml @@ -13,16 +13,18 @@ treeherder: 'Fxfn-r-e10s': 'Firefox functional tests (remote) with e10s' 'M': 'Mochitests' 'M-e10s': 'Mochitests with e10s' 'M-V': 'Mochitests on Valgrind' 'R': 'Reftests' 'R-e10s': 'Reftests with e10s' 'Rap': 'Raptor performance tests on Firefox' 'Rap-e10s': 'Raptor performance tests on Firefox with e10s' + 'Rap-C': 'Raptor performance tests on Google Chrome' + 'Rap-C-e10s': 'Raptor performance tests on Google Chrome with e10s' 'T': 'Talos performance tests' 'Tsd': 'Talos performance tests with Stylo disabled' 'Tss': 'Talos performance tests with Stylo sequential' 'T-e10s': 'Talos performance tests with e10s' 'Tsd-e10s': 'Talos performance tests with e10s, Stylo disabled' 'Tss-e10s': 'Talos performance tests with e10s, Stylo sequential' 'T-P-e10s': 'Talos performance tests with e10s and gecko profiling' 'tt-c': 'Telemetry client marionette tests'
--- a/taskcluster/ci/test/raptor.yml +++ b/taskcluster/ci/test/raptor.yml @@ -31,22 +31,36 @@ raptor-firefox-tp6: treeherder-symbol: Rap(tp6) run-on-projects: by-test-platform: .*-qr/.*: ['try'] default: ['try'] max-run-time: 1200 mozharness: extra-options: - - --test=raptor-firefox-tp6 + - --test=raptor-tp6 raptor-firefox-speedometer: description: "Raptor Firefox speedometer" try-name: raptor-firefox-speedometer treeherder-symbol: Rap(sp) run-on-projects: by-test-platform: .*-qr/.*: ['try'] default: ['try'] max-run-time: 1500 mozharness: extra-options: - --test=raptor-speedometer + +raptor-chrome-speedometer: + description: "Raptor Chrome speedometer" + try-name: raptor-chrome-speedometer + treeherder-symbol: Rap-C(sp) + run-on-projects: + by-test-platform: + .*-qr/.*: ['try'] + default: ['try'] + max-run-time: 1500 + mozharness: + extra-options: + - --test=raptor-speedometer + - --app=chrome
--- a/taskcluster/ci/test/test-sets.yml +++ b/taskcluster/ci/test/test-sets.yml @@ -278,16 +278,17 @@ macosx64-talos-profiling: # - talos-tps-profiling # Bug 1453007 times out macosx64-qr-tests: - reftest macosx64-raptor: - raptor-firefox-tp6 - raptor-firefox-speedometer + - raptor-chrome-speedometer linux32-tests: - cppunit - crashtest - firefox-ui-functional-local - firefox-ui-functional-remote - gtest - jittest
--- a/testing/mozbase/mozprofile/mozprofile/profile.py +++ b/testing/mozbase/mozprofile/mozprofile/profile.py @@ -491,18 +491,18 @@ class ChromeProfile(BaseProfile): class AddonManager(list): def install(self, addons): if isinstance(addons, string_types): addons = [addons] self.extend(addons) @classmethod def is_addon(self, addon): - # TODO Implement this properly - return os.path.exists(addon) + # Don't include testing/profiles on Google Chrome + return False def __init__(self, **kwargs): super(ChromeProfile, self).__init__(**kwargs) if self.create_new: self.profile = os.path.join(self.profile, 'Default') self._reset()
--- a/testing/mozbase/mozprofile/tests/test_profile.py +++ b/testing/mozbase/mozprofile/tests/test_profile.py @@ -84,15 +84,19 @@ def test_merge_profile(cls): prefs.update(Preferences.read_json(path)) except ValueError: prefs.update(Preferences.read_prefs(path)) assert 'foo' in prefs assert len(prefs) == len(profile.preference_file_names) + 1 assert all(name in prefs for name in profile.preference_file_names) - assert len(profile._addons) == 1 - assert profile._addons[0].endswith('empty.xpi') - assert os.path.exists(profile._addons[0]) + # for Google Chrome currently we ignore webext in profile prefs + if cls == Profile: + assert len(profile._addons) == 1 + assert profile._addons[0].endswith('empty.xpi') + assert os.path.exists(profile._addons[0]) + else: + assert len(profile._addons) == 0 if __name__ == '__main__': mozunit.main()
--- a/testing/mozharness/configs/raptor/mac_config.py +++ b/testing/mozharness/configs/raptor/mac_config.py @@ -21,16 +21,17 @@ config = { "http://pypi.pub.build.mozilla.org/pub", ], "pip_index": False, "title": os.uname()[1].lower().split('.')[0], "default_actions": [ "clobber", "download-and-extract", "populate-webroot", + "install-chrome", "create-virtualenv", "install", "run-tests", ], "run_cmd_checks_enabled": True, "preflight_run_cmd_suites": [ SCREEN_RESOLUTION_CHECK, ],
--- a/testing/mozharness/mozharness/mozilla/testing/raptor.py +++ b/testing/mozharness/mozharness/mozilla/testing/raptor.py @@ -5,16 +5,17 @@ from __future__ import absolute_import, print_function, unicode_literals import copy import json import os import re import sys import subprocess +import time from shutil import copyfile import mozharness from mozharness.base.errors import PythonErrorList from mozharness.base.log import OutputParser, DEBUG, ERROR, CRITICAL, INFO from mozharness.base.python import Python3Virtualenv @@ -22,16 +23,17 @@ from mozharness.mozilla.testing.testbase from mozharness.base.vcs.vcsbase import MercurialScript from mozharness.mozilla.testing.codecoverage import ( CodeCoverageMixin, code_coverage_config_options ) scripts_path = os.path.abspath(os.path.dirname(os.path.dirname(mozharness.__file__))) external_tools_path = os.path.join(scripts_path, 'external_tools') +here = os.path.abspath(os.path.dirname(__file__)) RaptorErrorList = PythonErrorList + [ {'regex': re.compile(r'''run-as: Package '.*' is unknown'''), 'level': DEBUG}, {'substr': r'''FAIL: Busted:''', 'level': CRITICAL}, {'substr': r'''FAIL: failed to cleanup''', 'level': ERROR}, {'substr': r'''erfConfigurator.py: Unknown error''', 'level': CRITICAL}, {'substr': r'''raptorError''', 'level': CRITICAL}, {'regex': re.compile(r'''No machine_name called '.*' can be found'''), 'level': CRITICAL}, @@ -47,73 +49,90 @@ class Raptor(TestingMixin, MercurialScri install and run raptor tests """ config_options = [ [["--test"], {"action": "store", "dest": "test", "help": "Raptor test to run" }], + [["--app"], + {"default": "firefox", + "choices": ["firefox", "chrome"], + "dest": "app", + "help": "name of the application we are testing (default: firefox)" + }], [["--branch-name"], {"action": "store", "dest": "branch", "help": "branch running against" }], [["--add-option"], {"action": "extend", - "dest": "raptor_extra_options", + "dest": "raptor_cmd_line_args", "default": None, "help": "extra options to raptor" }], ] + testing_config_options + copy.deepcopy(code_coverage_config_options) def __init__(self, **kwargs): kwargs.setdefault('config_options', self.config_options) kwargs.setdefault('all_actions', ['clobber', 'download-and-extract', 'populate-webroot', + 'install-chrome', 'create-virtualenv', 'install', 'run-tests', ]) kwargs.setdefault('default_actions', ['clobber', 'download-and-extract', 'populate-webroot', + 'install-chrome', 'create-virtualenv', 'install', 'run-tests', ]) kwargs.setdefault('config', {}) super(Raptor, self).__init__(**kwargs) self.workdir = self.query_abs_dirs()['abs_work_dir'] # convenience self.run_local = self.config.get('run_local') + + # app (browser testing on) defaults to firefox + self.app = "firefox" + + if self.run_local: + # raptor initiated locally, get app from command line args + # which are passed in from mach inside 'raptor_cmd_line_args' + self.app = "firefox" + if 'raptor_cmd_line_args' in self.config: + for next_arg in self.config['raptor_cmd_line_args']: + if "chrome" in next_arg: + self.app = "chrome" + break + else: + # raptor initiated in production via mozharness + self.test = self.config['test'] + self.app = self.config.get("app", "firefox") + self.installer_url = self.config.get("installer_url") self.raptor_json_url = self.config.get("raptor_json_url") self.raptor_json = self.config.get("raptor_json") self.raptor_json_config = self.config.get("raptor_json_config") self.repo_path = self.config.get("repo_path") self.obj_path = self.config.get("obj_path") - self.tests = None + self.test = None self.gecko_profile = self.config.get('gecko_profile') self.gecko_profile_interval = self.config.get('gecko_profile_interval') - # some platforms download a mitmproxy release binary - self.mitmproxy_rel_bin = None - # zip file found on tooltool that contains all of the mitmproxy recordings - self.mitmproxy_pageset = None - # files inside the recording set - self.mitmproxy_recordings_file_list = self.config.get('mitmproxy', None) - # path to mitmdump tool itself, in py3 venv - self.mitmdump = None # We accept some configuration options from the try commit message in the - # format mozharness: <options> - # Example try commit message: - # mozharness: --geckoProfile try: <stuff> + # format mozharness: <options>. Example try commit message: mozharness: + # --geckoProfile try: <stuff> def query_gecko_profile_options(self): gecko_results = [] # if gecko_profile is set, we add that to the raptor options if self.gecko_profile: gecko_results.append('--geckoProfile') if self.gecko_profile_interval: gecko_results.extend( ['--geckoProfileInterval', str(self.gecko_profile_interval)] @@ -125,29 +144,87 @@ class Raptor(TestingMixin, MercurialScri return self.abs_dirs abs_dirs = super(Raptor, self).query_abs_dirs() abs_dirs['abs_blob_upload_dir'] = os.path.join(abs_dirs['abs_work_dir'], 'blobber_upload_dir') abs_dirs['abs_test_install_dir'] = os.path.join(abs_dirs['abs_work_dir'], 'tests') self.abs_dirs = abs_dirs return self.abs_dirs + def install_chrome(self): + # temporary hack to install google chrome in production; until chrome is in our CI + if self.app != "chrome": + self.info("Google Chrome is not required") + return + + if self.config.get("run_local"): + self.info("expecting Google Chrome to be pre-installed locally") + return + + chrome_url = "https://dl.google.com/chrome/mac/stable/GGRO/googlechrome.dmg" + # in production we can put the chrome build in mozharness/mozilla/testing/chrome + self.chrome_dest = os.path.join(here, 'chrome') + chrome_dmg = os.path.join(self.chrome_dest, 'googlechrome.dmg') + + self.info("installing google chrome - temporary install hack") + self.info("chrome_dest is: %s" % self.chrome_dest) + + self.chrome_path = os.path.join(self.chrome_dest, 'Google Chrome.app', + 'Contents', 'MacOS', 'Google Chrome') + + if os.path.exists(self.chrome_path): + self.info("google chrome binary already exists at: %s" % self.chrome_path) + return + + if not os.path.exists(chrome_dmg): + # download the chrome dmg + self.download_file(chrome_url, parent_dir=self.chrome_dest) + + command = ["open", "googlechrome.dmg"] + return_code = self.run_command(command, cwd=self.chrome_dest) + if return_code not in [0]: + self.info("abort: failed to open %s/googlechrome.dmg" % self.chrome_dest) + return + # give 30 sec for open cmd to finish + time.sleep(30) + + # now that the googlechrome dmg is mounted, extract/copy app from mnt to our folder + command = ["cp", "-r", "/Volumes/Google Chrome/Google Chrome.app", "."] + return_code = self.run_command(command, cwd=self.chrome_dest) + if return_code not in [0]: + self.info("abort: failed to open %s/googlechrome.dmg" % self.chrome_dest) + return + + # now ensure chrome binary exists + if os.path.exists(self.chrome_path): + self.info("successfully installed Google Chrome to: %s" % self.chrome_path) + else: + self.info("abort: failed to install Google Chrome") + def raptor_options(self, args=None, **kw): """return options to raptor""" - # binary path - binary_path = self.binary_path or self.config.get('binary_path') - if not binary_path: - msg = """Raptor requires a path to the binary. You can specify binary_path or add - download-and-extract to your action list.""" - self.fatal(msg) - # raptor options - if binary_path.endswith('.exe'): - binary_path = binary_path[:-4] options = [] - kw_options = {'binary': binary_path} + kw_options = {} + + # binary path; if testing on firefox the binary path already came from mozharness/pro; + # otherwise the binary path is forwarded from cmd line arg (raptor_cmd_line_args) + kw_options['app'] = self.app + if self.app == "firefox": + binary_path = self.binary_path or self.config.get('binary_path') + if not binary_path: + self.fatal("Raptor requires a path to the binary.") + if binary_path.endswith('.exe'): + binary_path = binary_path[:-4] + kw_options['binary'] = binary_path + else: + if not self.run_local: + # in production we aready installed google chrome, so set the binary path for arg + # when running locally a --binary arg as passed in, already in raptor_cmd_line_args + kw_options['binary'] = self.chrome_path + # options overwritten from **kw if 'test' in self.config: kw_options['test'] = self.config['test'] if self.config.get('branch'): kw_options['branchName'] = self.config['branch'] if self.symbols_path: kw_options['symbolsPath'] = self.symbols_path if self.config.get('obj_path', None) is not None: @@ -155,44 +232,32 @@ class Raptor(TestingMixin, MercurialScri kw_options.update(kw) # configure profiling options options.extend(self.query_gecko_profile_options()) # extra arguments if args is not None: options += args if self.config.get('run_local', False): options.extend(['--run-local']) - if 'raptor_extra_options' in self.config: - options += self.config['raptor_extra_options'] + if 'raptor_cmd_line_args' in self.config: + options += self.config['raptor_cmd_line_args'] if self.config.get('code_coverage', False): options.extend(['--code-coverage']) for key, value in kw_options.items(): options.extend(['--%s' % key, value]) + return options def populate_webroot(self): """Populate the production test slaves' webroots""" self.raptor_path = os.path.join( self.query_abs_dirs()['abs_test_install_dir'], 'raptor' ) - if self.config.get('run_local'): - # raptor initiated locally, get and verify test from cmd line self.raptor_path = os.path.join(self.repo_path, 'testing', 'raptor') - if 'raptor_extra_options' in self.config: - if '--test' in self.config['raptor_extra_options']: - # --test specified, get test from cmd line and ensure is valid - test_name_index = self.config['raptor_extra_options'].index('--test') + 1 - if test_name_index < len(self.config['raptor_extra_options']): - self.test = self.config['raptor_extra_options'][test_name_index] - else: - self.fatal("Test name not provided") - else: - # raptor initiated in production via mozharness - self.test = self.config['test'] # Action methods. {{{1 # clobber defined in BaseScript def download_and_extract(self, extract_dirs=None, suite_categories=None): return super(Raptor, self).download_and_extract( suite_categories=['common', 'raptor'] )
--- a/testing/raptor/mach_commands.py +++ b/testing/raptor/mach_commands.py @@ -44,34 +44,34 @@ class RaptorRunner(MozbuildObject): self.virtualenv_script = os.path.join(self.topsrcdir, 'third_party', 'python', 'virtualenv', 'virtualenv.py') self.virtualenv_path = os.path.join(self._topobjdir, 'testing', 'raptor-venv') self.python_interp = sys.executable self.raptor_args = raptor_args def make_config(self): - default_actions = ['populate-webroot', 'create-virtualenv', 'run-tests'] + default_actions = ['populate-webroot', 'install-chrome', 'create-virtualenv', 'run-tests'] self.config = { 'run_local': True, 'binary_path': self.binary_path, 'repo_path': self.topsrcdir, 'raptor_path': self.raptor_dir, 'obj_path': self.topobjdir, 'log_name': 'raptor', 'virtualenv_path': self.virtualenv_path, 'pypi_url': 'http://pypi.python.org/simple', 'base_work_dir': self.mozharness_dir, 'exes': { 'python': self.python_interp, 'virtualenv': [self.python_interp, self.virtualenv_script], }, 'title': socket.gethostname(), 'default_actions': default_actions, - 'raptor_extra_options': self.raptor_args, + 'raptor_cmd_line_args': self.raptor_args, 'python3_manifest': { 'win32': 'python3.manifest', 'win64': 'python3_x64.manifest', } } def make_args(self): self.args = {
--- a/testing/raptor/raptor/cmdline.py +++ b/testing/raptor/raptor/cmdline.py @@ -10,37 +10,39 @@ from mozlog.commandline import add_loggi def create_parser(mach_interface=False): parser = argparse.ArgumentParser() add_arg = parser.add_argument add_arg('-t', '--test', required=True, dest='test', help="name of raptor test to run") + add_arg('--app', default='firefox', dest='app', + help="name of the application we are testing (default: firefox)", + choices=['firefox', 'chrome']) + add_arg('-b', '--binary', dest='binary', + help="path to the browser executable that we are testing") if not mach_interface: - add_arg('--app', default='firefox', dest='app', - help="name of the application we are testing (default: firefox)", - choices=['firefox', 'chrome']) - add_arg('-b', '--binary', required=True, dest='binary', - help="path to the browser executable that we are testing") - add_arg('--branchName', dest="branch_name", default=None, + add_arg('--branchName', dest="branch_name", default='', help="Name of the branch we are testing on") add_arg('--symbolsPath', dest='symbols_path', help="Path to the symbols for the build we are testing") add_arg('--run-local', dest="run_local", default=False, action="store_true", help="Flag that indicates if raptor is running locally or in production") add_arg('--obj-path', dest="obj_path", default=None, help="Browser build obj_path (received when running in production)") add_logging_group(parser) return parser def verify_options(parser, args): ctx = vars(args) + if args.binary is None: + parser.error("--binary is required!") if not os.path.isfile(args.binary): parser.error("{binary} does not exist!".format(**ctx)) def parse_args(argv=None): parser = create_parser() args = parser.parse_args(argv)
--- a/testing/raptor/raptor/raptor.ini +++ b/testing/raptor/raptor/raptor.ini @@ -1,3 +1,3 @@ # raptor tests -[include:tests/raptor-firefox-tp6.ini] +[include:tests/raptor-tp6.ini] [include:tests/raptor-speedometer.ini]
--- a/testing/raptor/raptor/raptor.py +++ b/testing/raptor/raptor/raptor.py @@ -115,17 +115,19 @@ class Raptor(object): test['name'], self.control_server.port, benchmark_port) # must intall raptor addon each time because we dynamically update some content raptor_webext = os.path.join(webext_dir, 'raptor') self.log.info("installing webext %s" % raptor_webext) self.profile.addons.install(raptor_webext) - webext_id = self.profile.addons.addon_details(raptor_webext)['id'] + # on firefox we can get an addon id; chrome addon actually is just cmd line arg + if self.config['app'] == "firefox": + webext_id = self.profile.addons.addon_details(raptor_webext)['id'] # some tests require tools to playback the test pages if test.get('playback', None) is not None: self.get_playback_config(test) # startup the playback tool self.playback = get_playback(self.config) self.runner.start() @@ -141,18 +143,20 @@ class Raptor(object): self.runner.check_for_crashes() except NotImplementedError: # not implemented for Chrome pass if self.playback is not None: self.playback.stop() # remove the raptor webext; as it must be reloaded with each subtest anyway - self.log.info("removing webext %s" % raptor_webext) - self.profile.addons.remove_addon(webext_id) + # applies to firefox only; chrome the addon is actually just cmd line arg + if self.config['app'] == "firefox": + self.log.info("removing webext %s" % raptor_webext) + self.profile.addons.remove_addon(webext_id) if self.runner.is_running(): self.log("Application timed out after {} seconds".format(timeout)) self.runner.stop() def process_results(self): # when running locally output results in build/raptor.json; when running # in production output to a local.json to be turned into tc job artifact @@ -174,16 +178,18 @@ class Raptor(object): self.log.info("finished") def main(args=sys.argv[1:]): args = parse_args() commandline.setup_logging('raptor', args, {'tbpl': sys.stdout}) LOG = get_default_logger(component='raptor-main') + LOG.info("received command line arguments: %s" % str(args)) + # if a test name specified on command line, and it exists, just run that one # otherwise run all available raptor tests that are found for this browser raptor_test_list = get_raptor_test_list(args) # ensure we have at least one valid test to run if len(raptor_test_list) == 0: LOG.critical("abort: no tests found") sys.exit(1)
deleted file mode 100644 --- a/testing/raptor/raptor/tests/raptor-firefox-tp6.ini +++ /dev/null @@ -1,39 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# raptor tp6 on firefox - -[DEFAULT] -apps = firefox -type = pageload -playback = mitmproxy -playback_binary_manifest = mitmproxy-rel-bin-osx.manifest -playback_binary_zip_mac = mitmproxy-2.0.2-osx.tar.gz -playback_pageset_manifest = mitmproxy-playback-set.manifest -playback_pageset_zip_mac = mitmproxy-recording-set-win10.zip -page_cycles = 25 -unit = ms -lower_is_better = true -alert_threshold = 2.0 - -[raptor-firefox-tp6-amazon] -test_url = https://www.amazon.com/s/url=search-alias%3Daps&field-keywords=laptop -playback_recordings = mitmproxy-recording-amazon.mp -measure = fnbpaint - -[raptor-firefox-tp6-facebook] -test_url = https://www.facebook.com -playback_recordings = mitmproxy-recording-facebook.mp -measure = fnbpaint - -[raptor-firefox-tp6-google] -test_url = https://www.google.com/#hl=en&q=barack+obama -playback_recordings = mitmproxy-recording-google.mp -measure = fnbpaint, hero -hero = hero - -[raptor-firefox-tp6-youtube] -test_url = https://www.youtube.com -playback_recordings = mitmproxy-recording-youtube.mp -measure = fnbpaint
--- a/testing/raptor/raptor/tests/raptor-speedometer.ini +++ b/testing/raptor/raptor/tests/raptor-speedometer.ini @@ -1,15 +1,20 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # speedometer benchmark for firefox and chrome -[raptor-speedometer] -apps = firefox +[DEFAULT] type = benchmark test_url = http://localhost:<port>/Speedometer/index.html?raptor -page_cycles = 5 +page_cycles = 1 page_timeout = 120000 unit = score lower_is_better = false alert_threshold = 2.0 + +[raptor-speedometer-firefox] +apps = firefox + +[raptor-speedometer-chrome] +apps = chrome
new file mode 100644 --- /dev/null +++ b/testing/raptor/raptor/tests/raptor-tp6.ini @@ -0,0 +1,67 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# raptor tp6 + +[DEFAULT] +type = pageload +playback = mitmproxy +playback_binary_manifest = mitmproxy-rel-bin-osx.manifest +playback_binary_zip_mac = mitmproxy-2.0.2-osx.tar.gz +playback_pageset_manifest = mitmproxy-playback-set.manifest +playback_pageset_zip_mac = mitmproxy-recording-set-win10.zip +page_cycles = 25 +unit = ms +lower_is_better = true +alert_threshold = 2.0 + +[raptor-firefox-tp6-amazon] +apps = firefox +test_url = https://www.amazon.com/s/url=search-alias%3Daps&field-keywords=laptop +playback_recordings = mitmproxy-recording-amazon.mp +measure = fnbpaint + +[raptor-firefox-tp6-facebook] +apps = firefox +test_url = https://www.facebook.com +playback_recordings = mitmproxy-recording-facebook.mp +measure = fnbpaint + +[raptor-firefox-tp6-google] +apps = firefox +test_url = https://www.google.com/#hl=en&q=barack+obama +playback_recordings = mitmproxy-recording-google.mp +measure = fnbpaint, hero +hero = hero + +[raptor-firefox-tp6-youtube] +apps = firefox +test_url = https://www.youtube.com +playback_recordings = mitmproxy-recording-youtube.mp +measure = fnbpaint + +[raptor-chrome-tp6-amazon] +apps = chrome +test_url = https://www.amazon.com/s/url=search-alias%3Daps&field-keywords=laptop +playback_recordings = mitmproxy-recording-amazon.mp +measure = fcp + +[raptor-chrome-tp6-facebook] +apps = chrome +test_url = https://www.facebook.com +playback_recordings = mitmproxy-recording-facebook.mp +measure = fcp + +[raptor-chrome-tp6-google] +apps = chrome +test_url = https://www.google.com/#hl=en&q=barack+obama +playback_recordings = mitmproxy-recording-google.mp +measure = fcp, hero +hero = hero + +[raptor-chrome-tp6-youtube] +apps = chrome +test_url = https://www.youtube.com +playback_recordings = mitmproxy-recording-youtube.mp +measure = fcp
--- a/testing/raptor/webext/raptor/manifest.json +++ b/testing/raptor/webext/raptor/manifest.json @@ -11,16 +11,17 @@ "background": { "scripts": ["auto_gen_test_config.js", "runner.js"] }, "content_scripts": [ { "matches": ["*://*.amazon.com/*", "*://*.facebook.com/*", "*://*.google.com/*", + "*://*.google.ca/*", "*://*.youtube.com/*"], "js": ["measure.js"] }, { "matches": ["*://*/Speedometer/index.html*"], "js": ["benchmark-relay.js"] } ],
--- a/toolkit/content/browser-content.js +++ b/toolkit/content/browser-content.js @@ -5,366 +5,62 @@ /* eslint-env mozilla/frame-script */ /* eslint no-unused-vars: ["error", {args: "none"}] */ /* global sendAsyncMessage */ ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); +ChromeUtils.defineModuleGetter(this, "AutoScrollController", + "resource://gre/modules/AutoScrollController.jsm"); ChromeUtils.defineModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); ChromeUtils.defineModuleGetter(this, "SelectContentHelper", "resource://gre/modules/SelectContentHelper.jsm"); ChromeUtils.defineModuleGetter(this, "FindContent", "resource://gre/modules/FindContent.jsm"); ChromeUtils.defineModuleGetter(this, "PrintingContent", "resource://gre/modules/PrintingContent.jsm"); ChromeUtils.defineModuleGetter(this, "RemoteFinder", "resource://gre/modules/RemoteFinder.jsm"); XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent", "resource://gre/modules/SelectionSourceContent.jsm"); +XPCOMUtils.defineLazyProxy(this, "DateTimePickerContent", () => { + let tmp = {}; + ChromeUtils.import("resource://gre/modules/DateTimePickerContent.jsm", tmp); + return new tmp.DateTimePickerContent(this); +}); + var global = this; // Lazily load the finder code addMessageListener("Finder:Initialize", function() { let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {}); new RemoteFinderListener(global); }); -var ClickEventHandler = { - init: function init() { - this._scrollable = null; - this._scrolldir = ""; - this._startX = null; - this._startY = null; - this._screenX = null; - this._screenY = null; - this._lastFrame = null; - this._autoscrollHandledByApz = false; - this._scrollId = null; - this.autoscrollLoop = this.autoscrollLoop.bind(this); - - Services.els.addSystemEventListener(global, "mousedown", this, true); - - addMessageListener("Autoscroll:Stop", this); - }, - - isAutoscrollBlocker(node) { - let mmPaste = Services.prefs.getBoolPref("middlemouse.paste"); - let mmScrollbarPosition = Services.prefs.getBoolPref("middlemouse.scrollbarPosition"); - - while (node) { - if ((node instanceof content.HTMLAnchorElement || node instanceof content.HTMLAreaElement) && - node.hasAttribute("href")) { - return true; +var AutoScrollListener = { + handleEvent(event) { + if (event.isTrusted & + !event.defaultPrevented && + event.button == 1) { + if (!this._controller) { + this._controller = new AutoScrollController(global); } - - if (mmPaste && (node instanceof content.HTMLInputElement || - node instanceof content.HTMLTextAreaElement)) { - return true; - } - - if (node instanceof content.XULElement && mmScrollbarPosition - && (node.localName == "scrollbar" || node.localName == "scrollcorner")) { - return true; - } - - node = node.parentNode; - } - return false; - }, - - isScrollableElement(aNode) { - if (aNode instanceof content.HTMLElement) { - return !(aNode instanceof content.HTMLSelectElement) || aNode.multiple; - } - - return aNode instanceof content.XULElement; - }, - - getXBLNodes(parent, array) { - let anonNodes = content.document.getAnonymousNodes(parent); - let nodes = Array.from(anonNodes || parent.childNodes || []); - for (let node of nodes) { - if (node.nodeName == "children") { - return true; - } - if (this.getXBLNodes(node, array)) { - array.push(node); - return true; - } + this._controller.handleEvent(event); } - return false; - }, - - * parentNodeIterator(aNode) { - while (aNode) { - yield aNode; - - let parent = aNode.parentNode; - if (parent && parent instanceof content.XULElement) { - let anonNodes = content.document.getAnonymousNodes(parent); - if (anonNodes && !Array.from(anonNodes).includes(aNode)) { - // XBL elements are skipped by parentNode property. - // Yield elements between parent and <children> here. - let nodes = []; - this.getXBLNodes(parent, nodes); - for (let node of nodes) { - yield node; - } - } - } - - aNode = parent; - } - }, - - findNearestScrollableElement(aNode) { - // this is a list of overflow property values that allow scrolling - const scrollingAllowed = ["scroll", "auto"]; - - // go upward in the DOM and find any parent element that has a overflow - // area and can therefore be scrolled - this._scrollable = null; - for (let node of this.parentNodeIterator(aNode)) { - // do not use overflow based autoscroll for <html> and <body> - // Elements or non-html/non-xul elements such as svg or Document nodes - // also make sure to skip select elements that are not multiline - if (!this.isScrollableElement(node)) { - continue; - } - - var overflowx = node.ownerGlobal - .getComputedStyle(node) - .getPropertyValue("overflow-x"); - var overflowy = node.ownerGlobal - .getComputedStyle(node) - .getPropertyValue("overflow-y"); - // we already discarded non-multiline selects so allow vertical - // scroll for multiline ones directly without checking for a - // overflow property - var scrollVert = node.scrollTopMax && - (node instanceof content.HTMLSelectElement || - scrollingAllowed.includes(overflowy)); - - // do not allow horizontal scrolling for select elements, it leads - // to visual artifacts and is not the expected behavior anyway - if (!(node instanceof content.HTMLSelectElement) && - node.scrollLeftMin != node.scrollLeftMax && - scrollingAllowed.includes(overflowx)) { - this._scrolldir = scrollVert ? "NSEW" : "EW"; - this._scrollable = node; - break; - } else if (scrollVert) { - this._scrolldir = "NS"; - this._scrollable = node; - break; - } - } - - if (!this._scrollable) { - this._scrollable = aNode.ownerGlobal; - if (this._scrollable.scrollMaxX != this._scrollable.scrollMinX) { - this._scrolldir = this._scrollable.scrollMaxY != - this._scrollable.scrollMinY ? "NSEW" : "EW"; - } else if (this._scrollable.scrollMaxY != this._scrollable.scrollMinY) { - this._scrolldir = "NS"; - } else if (this._scrollable.frameElement) { - this.findNearestScrollableElement(this._scrollable.frameElement); - } else { - this._scrollable = null; // abort scrolling - } - } - }, - - startScroll(event) { - - this.findNearestScrollableElement(event.originalTarget); - - if (!this._scrollable) - return; - - // In some configurations like Print Preview, content.performance - // (which we use below) is null. Autoscrolling is broken in Print - // Preview anyways (see bug 1393494), so just don't start it at all. - if (!content.performance) - return; + } +}; +Services.els.addSystemEventListener(global, "mousedown", AutoScrollListener, true); - let domUtils = content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let scrollable = this._scrollable; - if (scrollable instanceof Ci.nsIDOMWindow) { - // getViewId() needs an element to operate on. - scrollable = scrollable.document.documentElement; - } - this._scrollId = null; - try { - this._scrollId = domUtils.getViewId(scrollable); - } catch (e) { - // No view ID - leave this._scrollId as null. Receiving side will check. - } - let presShellId = domUtils.getPresShellId(); - let [result] = sendSyncMessage("Autoscroll:Start", - {scrolldir: this._scrolldir, - screenX: event.screenX, - screenY: event.screenY, - scrollId: this._scrollId, - presShellId}); - if (!result.autoscrollEnabled) { - this._scrollable = null; - return; - } - - Services.els.addSystemEventListener(global, "mousemove", this, true); - addEventListener("pagehide", this, true); - - this._ignoreMouseEvents = true; - this._startX = event.screenX; - this._startY = event.screenY; - this._screenX = event.screenX; - this._screenY = event.screenY; - this._scrollErrorX = 0; - this._scrollErrorY = 0; - this._autoscrollHandledByApz = result.usingApz; - - if (!result.usingApz) { - // If the browser didn't hand the autoscroll off to APZ, - // scroll here in the main thread. - this.startMainThreadScroll(); - } else { - // Even if the browser did hand the autoscroll to APZ, - // APZ might reject it in which case it will notify us - // and we need to take over. - Services.obs.addObserver(this, "autoscroll-rejected-by-apz"); - } - }, - - startMainThreadScroll() { - this._lastFrame = content.performance.now(); - content.requestAnimationFrame(this.autoscrollLoop); - }, - - stopScroll() { - if (this._scrollable) { - this._scrollable.mozScrollSnap(); - this._scrollable = null; - - Services.els.removeSystemEventListener(global, "mousemove", this, true); - removeEventListener("pagehide", this, true); - if (this._autoscrollHandledByApz) { - Services.obs.removeObserver(this, "autoscroll-rejected-by-apz"); - } - } - }, - - accelerate(curr, start) { - const speed = 12; - var val = (curr - start) / speed; - - if (val > 1) - return val * Math.sqrt(val) - 1; - if (val < -1) - return val * Math.sqrt(-val) + 1; - return 0; - }, - - roundToZero(num) { - if (num > 0) - return Math.floor(num); - return Math.ceil(num); - }, - - autoscrollLoop(timestamp) { - if (!this._scrollable) { - // Scrolling has been canceled - return; - } - - // avoid long jumps when the browser hangs for more than - // |maxTimeDelta| ms - const maxTimeDelta = 100; - var timeDelta = Math.min(maxTimeDelta, timestamp - this._lastFrame); - // we used to scroll |accelerate()| pixels every 20ms (50fps) - var timeCompensation = timeDelta / 20; - this._lastFrame = timestamp; - - var actualScrollX = 0; - var actualScrollY = 0; - // don't bother scrolling vertically when the scrolldir is only horizontal - // and the other way around - if (this._scrolldir != "EW") { - var y = this.accelerate(this._screenY, this._startY) * timeCompensation; - var desiredScrollY = this._scrollErrorY + y; - actualScrollY = this.roundToZero(desiredScrollY); - this._scrollErrorY = (desiredScrollY - actualScrollY); - } - if (this._scrolldir != "NS") { - var x = this.accelerate(this._screenX, this._startX) * timeCompensation; - var desiredScrollX = this._scrollErrorX + x; - actualScrollX = this.roundToZero(desiredScrollX); - this._scrollErrorX = (desiredScrollX - actualScrollX); - } - - this._scrollable.scrollBy({ - left: actualScrollX, - top: actualScrollY, - behavior: "instant" - }); - - content.requestAnimationFrame(this.autoscrollLoop); - }, - - handleEvent(event) { - if (event.type == "mousemove") { - this._screenX = event.screenX; - this._screenY = event.screenY; - } else if (event.type == "mousedown") { - if (event.isTrusted & - !event.defaultPrevented && - event.button == 1 && - !this._scrollable && - !this.isAutoscrollBlocker(event.originalTarget)) { - this.startScroll(event); - } - } else if (event.type == "pagehide") { - if (this._scrollable) { - var doc = - this._scrollable.ownerDocument || this._scrollable.document; - if (doc == event.target) { - sendAsyncMessage("Autoscroll:Cancel"); - } - } - } - }, - - receiveMessage(msg) { - switch (msg.name) { - case "Autoscroll:Stop": { - this.stopScroll(); - break; - } - } - }, - - observe(subject, topic, data) { - if (topic === "autoscroll-rejected-by-apz") { - // The caller passes in the scroll id via 'data'. - if (data == this._scrollId) { - this._autoscrollHandledByApz = false; - this.startMainThreadScroll(); - Services.obs.removeObserver(this, "autoscroll-rejected-by-apz"); - } - } - }, -}; -ClickEventHandler.init(); +addEventListener("MozOpenDateTimePicker", DateTimePickerContent); var PopupBlocking = { popupData: null, popupDataInternal: null, init() { addEventListener("DOMPopupBlocked", this, true); addEventListener("pageshow", this, true); @@ -1108,180 +804,16 @@ let AutoCompletePopup = { } return results; }, }; AutoCompletePopup.init(); -/** - * DateTimePickerListener is the communication channel between the input box - * (content) for date/time input types and its picker (chrome). - */ -let DateTimePickerListener = { - /** - * On init, just listen for the event to open the picker, once the picker is - * opened, we'll listen for update and close events. - */ - init() { - addEventListener("MozOpenDateTimePicker", this); - this._inputElement = null; - - addEventListener("unload", () => { - this.uninit(); - }); - }, - - uninit() { - removeEventListener("MozOpenDateTimePicker", this); - this._inputElement = null; - }, - - /** - * Cleanup function called when picker is closed. - */ - close() { - this.removeListeners(); - this._inputElement.setDateTimePickerState(false); - this._inputElement = null; - }, - - /** - * Called after picker is opened to start listening for input box update - * events. - */ - addListeners() { - addEventListener("MozUpdateDateTimePicker", this); - addEventListener("MozCloseDateTimePicker", this); - addEventListener("pagehide", this); - - addMessageListener("FormDateTime:PickerValueChanged", this); - addMessageListener("FormDateTime:PickerClosed", this); - }, - - /** - * Stop listeneing for events when picker is closed. - */ - removeListeners() { - removeEventListener("MozUpdateDateTimePicker", this); - removeEventListener("MozCloseDateTimePicker", this); - removeEventListener("pagehide", this); - - removeMessageListener("FormDateTime:PickerValueChanged", this); - removeMessageListener("FormDateTime:PickerClosed", this); - }, - - /** - * Helper function that returns the CSS direction property of the element. - */ - getComputedDirection(aElement) { - return aElement.ownerGlobal.getComputedStyle(aElement) - .getPropertyValue("direction"); - }, - - /** - * Helper function that returns the rect of the element, which is the position - * relative to the left/top of the content area. - */ - getBoundingContentRect(aElement) { - return BrowserUtils.getElementBoundingRect(aElement); - }, - - getTimePickerPref() { - return Services.prefs.getBoolPref("dom.forms.datetime.timepicker"); - }, - - /** - * nsIMessageListener. - */ - receiveMessage(aMessage) { - switch (aMessage.name) { - case "FormDateTime:PickerClosed": { - this.close(); - break; - } - case "FormDateTime:PickerValueChanged": { - this._inputElement.updateDateTimeInputBox(aMessage.data); - break; - } - default: - break; - } - }, - - /** - * nsIDOMEventListener, for chrome events sent by the input element and other - * DOM events. - */ - handleEvent(aEvent) { - switch (aEvent.type) { - case "MozOpenDateTimePicker": { - // Time picker is disabled when preffed off - if (!(aEvent.originalTarget instanceof content.HTMLInputElement) || - (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())) { - return; - } - - if (this._inputElement) { - // This happens when we're trying to open a picker when another picker - // is still open. We ignore this request to let the first picker - // close gracefully. - return; - } - - this._inputElement = aEvent.originalTarget; - this._inputElement.setDateTimePickerState(true); - this.addListeners(); - - let value = this._inputElement.getDateTimeInputBoxValue(); - sendAsyncMessage("FormDateTime:OpenPicker", { - rect: this.getBoundingContentRect(this._inputElement), - dir: this.getComputedDirection(this._inputElement), - type: this._inputElement.type, - detail: { - // Pass partial value if it's available, otherwise pass input - // element's value. - value: Object.keys(value).length > 0 ? value - : this._inputElement.value, - min: this._inputElement.getMinimum(), - max: this._inputElement.getMaximum(), - step: this._inputElement.getStep(), - stepBase: this._inputElement.getStepBase(), - }, - }); - break; - } - case "MozUpdateDateTimePicker": { - let value = this._inputElement.getDateTimeInputBoxValue(); - value.type = this._inputElement.type; - sendAsyncMessage("FormDateTime:UpdatePicker", { value }); - break; - } - case "MozCloseDateTimePicker": { - sendAsyncMessage("FormDateTime:ClosePicker"); - this.close(); - break; - } - case "pagehide": { - if (this._inputElement && - this._inputElement.ownerDocument == aEvent.target) { - sendAsyncMessage("FormDateTime:ClosePicker"); - this.close(); - } - break; - } - default: - break; - } - }, -}; - -DateTimePickerListener.init(); - addEventListener("mozshowdropdown", event => { if (!event.isTrusted) return; if (!SelectContentHelper.open) { new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this); } });
--- a/toolkit/content/widgets/datetimepopup.xml +++ b/toolkit/content/widgets/datetimepopup.xml @@ -28,17 +28,17 @@ </property> <field name="TIME_PICKER_WIDTH" readonly="true">"12em"</field> <field name="TIME_PICKER_HEIGHT" readonly="true">"21em"</field> <field name="DATE_PICKER_WIDTH" readonly="true">"23.1em"</field> <field name="DATE_PICKER_HEIGHT" readonly="true">"20.7em"</field> <constructor><![CDATA[ this.mozIntl = Cc["@mozilla.org/mozintl;1"] .getService(Ci.mozIMozIntl); - // Notify DateTimePickerHelper.jsm that binding is ready. + // Notify DateTimePickerParent.jsm that binding is ready. this.dispatchEvent(new CustomEvent("DateTimePickerBindingReady")); ]]></constructor> <method name="openPicker"> <parameter name="type"/> <parameter name="anchor"/> <parameter name="detail"/> <body><![CDATA[ this.type = type;
copy from toolkit/content/browser-content.js copy to toolkit/modules/AutoScrollController.jsm --- a/toolkit/content/browser-content.js +++ b/toolkit/modules/AutoScrollController.jsm @@ -1,64 +1,41 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* eslint-env mozilla/frame-script */ /* eslint no-unused-vars: ["error", {args: "none"}] */ -/* global sendAsyncMessage */ ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); -ChromeUtils.defineModuleGetter(this, "BrowserUtils", - "resource://gre/modules/BrowserUtils.jsm"); -ChromeUtils.defineModuleGetter(this, "SelectContentHelper", - "resource://gre/modules/SelectContentHelper.jsm"); -ChromeUtils.defineModuleGetter(this, "FindContent", - "resource://gre/modules/FindContent.jsm"); -ChromeUtils.defineModuleGetter(this, "PrintingContent", - "resource://gre/modules/PrintingContent.jsm"); -ChromeUtils.defineModuleGetter(this, "RemoteFinder", - "resource://gre/modules/RemoteFinder.jsm"); +var EXPORTED_SYMBOLS = ["AutoScrollController"]; -XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent", - "resource://gre/modules/SelectionSourceContent.jsm"); - -var global = this; - - -// Lazily load the finder code -addMessageListener("Finder:Initialize", function() { - let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {}); - new RemoteFinderListener(global); -}); - -var ClickEventHandler = { - init: function init() { +class AutoScrollController { + constructor(global) { this._scrollable = null; this._scrolldir = ""; this._startX = null; this._startY = null; this._screenX = null; this._screenY = null; this._lastFrame = null; this._autoscrollHandledByApz = false; this._scrollId = null; + this._global = global; this.autoscrollLoop = this.autoscrollLoop.bind(this); - Services.els.addSystemEventListener(global, "mousedown", this, true); - - addMessageListener("Autoscroll:Stop", this); - }, + global.addMessageListener("Autoscroll:Stop", this); + } isAutoscrollBlocker(node) { let mmPaste = Services.prefs.getBoolPref("middlemouse.paste"); let mmScrollbarPosition = Services.prefs.getBoolPref("middlemouse.scrollbarPosition"); + let content = node.ownerGlobal; while (node) { if ((node instanceof content.HTMLAnchorElement || node instanceof content.HTMLAreaElement) && node.hasAttribute("href")) { return true; } if (mmPaste && (node instanceof content.HTMLInputElement || @@ -69,42 +46,46 @@ var ClickEventHandler = { if (node instanceof content.XULElement && mmScrollbarPosition && (node.localName == "scrollbar" || node.localName == "scrollcorner")) { return true; } node = node.parentNode; } return false; - }, + } isScrollableElement(aNode) { + let content = aNode.ownerGlobal; if (aNode instanceof content.HTMLElement) { return !(aNode instanceof content.HTMLSelectElement) || aNode.multiple; } return aNode instanceof content.XULElement; - }, + } getXBLNodes(parent, array) { + let content = parent.ownerGlobal; let anonNodes = content.document.getAnonymousNodes(parent); let nodes = Array.from(anonNodes || parent.childNodes || []); for (let node of nodes) { if (node.nodeName == "children") { return true; } if (this.getXBLNodes(node, array)) { array.push(node); return true; } } return false; - }, + } * parentNodeIterator(aNode) { + let content = aNode.ownerGlobal; + while (aNode) { yield aNode; let parent = aNode.parentNode; if (parent && parent instanceof content.XULElement) { let anonNodes = content.document.getAnonymousNodes(parent); if (anonNodes && !Array.from(anonNodes).includes(aNode)) { // XBL elements are skipped by parentNode property. @@ -114,19 +95,21 @@ var ClickEventHandler = { for (let node of nodes) { yield node; } } } aNode = parent; } - }, + } findNearestScrollableElement(aNode) { + let content = aNode.ownerGlobal; + // this is a list of overflow property values that allow scrolling const scrollingAllowed = ["scroll", "auto"]; // go upward in the DOM and find any parent element that has a overflow // area and can therefore be scrolled this._scrollable = null; for (let node of this.parentNodeIterator(aNode)) { // do not use overflow based autoscroll for <html> and <body> @@ -172,25 +155,27 @@ var ClickEventHandler = { } else if (this._scrollable.scrollMaxY != this._scrollable.scrollMinY) { this._scrolldir = "NS"; } else if (this._scrollable.frameElement) { this.findNearestScrollableElement(this._scrollable.frameElement); } else { this._scrollable = null; // abort scrolling } } - }, + } startScroll(event) { this.findNearestScrollableElement(event.originalTarget); if (!this._scrollable) return; + let content = event.originalTarget.ownerGlobal; + // In some configurations like Print Preview, content.performance // (which we use below) is null. Autoscrolling is broken in Print // Preview anyways (see bug 1393494), so just don't start it at all. if (!content.performance) return; let domUtils = content.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); @@ -201,29 +186,29 @@ var ClickEventHandler = { } this._scrollId = null; try { this._scrollId = domUtils.getViewId(scrollable); } catch (e) { // No view ID - leave this._scrollId as null. Receiving side will check. } let presShellId = domUtils.getPresShellId(); - let [result] = sendSyncMessage("Autoscroll:Start", - {scrolldir: this._scrolldir, - screenX: event.screenX, - screenY: event.screenY, - scrollId: this._scrollId, - presShellId}); + let [result] = this._global.sendSyncMessage("Autoscroll:Start", + {scrolldir: this._scrolldir, + screenX: event.screenX, + screenY: event.screenY, + scrollId: this._scrollId, + presShellId}); if (!result.autoscrollEnabled) { this._scrollable = null; return; } - Services.els.addSystemEventListener(global, "mousemove", this, true); - addEventListener("pagehide", this, true); + Services.els.addSystemEventListener(this._global, "mousemove", this, true); + this._global.addEventListener("pagehide", this, true); this._ignoreMouseEvents = true; this._startX = event.screenX; this._startY = event.screenY; this._screenX = event.screenX; this._screenY = event.screenY; this._scrollErrorX = 0; this._scrollErrorY = 0; @@ -234,52 +219,53 @@ var ClickEventHandler = { // scroll here in the main thread. this.startMainThreadScroll(); } else { // Even if the browser did hand the autoscroll to APZ, // APZ might reject it in which case it will notify us // and we need to take over. Services.obs.addObserver(this, "autoscroll-rejected-by-apz"); } - }, + } startMainThreadScroll() { + let content = this._global.content; this._lastFrame = content.performance.now(); content.requestAnimationFrame(this.autoscrollLoop); - }, + } stopScroll() { if (this._scrollable) { this._scrollable.mozScrollSnap(); this._scrollable = null; - Services.els.removeSystemEventListener(global, "mousemove", this, true); - removeEventListener("pagehide", this, true); + Services.els.removeSystemEventListener(this._global, "mousemove", this, true); + this._global.removeEventListener("pagehide", this, true); if (this._autoscrollHandledByApz) { Services.obs.removeObserver(this, "autoscroll-rejected-by-apz"); } } - }, + } accelerate(curr, start) { const speed = 12; var val = (curr - start) / speed; if (val > 1) return val * Math.sqrt(val) - 1; if (val < -1) return val * Math.sqrt(-val) + 1; return 0; - }, + } roundToZero(num) { if (num > 0) return Math.floor(num); return Math.ceil(num); - }, + } autoscrollLoop(timestamp) { if (!this._scrollable) { // Scrolling has been canceled return; } // avoid long jumps when the browser hangs for more than @@ -308,1023 +294,51 @@ var ClickEventHandler = { } this._scrollable.scrollBy({ left: actualScrollX, top: actualScrollY, behavior: "instant" }); - content.requestAnimationFrame(this.autoscrollLoop); - }, + this._scrollable.ownerGlobal.requestAnimationFrame(this.autoscrollLoop); + } handleEvent(event) { if (event.type == "mousemove") { this._screenX = event.screenX; this._screenY = event.screenY; } else if (event.type == "mousedown") { - if (event.isTrusted & - !event.defaultPrevented && - event.button == 1 && - !this._scrollable && + if (!this._scrollable && !this.isAutoscrollBlocker(event.originalTarget)) { this.startScroll(event); } } else if (event.type == "pagehide") { if (this._scrollable) { var doc = this._scrollable.ownerDocument || this._scrollable.document; if (doc == event.target) { - sendAsyncMessage("Autoscroll:Cancel"); + this._global.sendAsyncMessage("Autoscroll:Cancel"); } } } - }, + } receiveMessage(msg) { switch (msg.name) { case "Autoscroll:Stop": { this.stopScroll(); break; } } - }, + } observe(subject, topic, data) { if (topic === "autoscroll-rejected-by-apz") { // The caller passes in the scroll id via 'data'. if (data == this._scrollId) { this._autoscrollHandledByApz = false; this.startMainThreadScroll(); Services.obs.removeObserver(this, "autoscroll-rejected-by-apz"); } } - }, -}; -ClickEventHandler.init(); - -var PopupBlocking = { - popupData: null, - popupDataInternal: null, - - init() { - addEventListener("DOMPopupBlocked", this, true); - addEventListener("pageshow", this, true); - addEventListener("pagehide", this, true); - - addMessageListener("PopupBlocking:UnblockPopup", this); - addMessageListener("PopupBlocking:GetBlockedPopupList", this); - }, - - receiveMessage(msg) { - switch (msg.name) { - case "PopupBlocking:UnblockPopup": { - let i = msg.data.index; - if (this.popupData && this.popupData[i]) { - let data = this.popupData[i]; - let internals = this.popupDataInternal[i]; - let dwi = internals.requestingWindow; - - // If we have a requesting window and the requesting document is - // still the current document, open the popup. - if (dwi && dwi.document == internals.requestingDocument) { - dwi.open(data.popupWindowURIspec, data.popupWindowName, data.popupWindowFeatures); - } - } - break; - } - - case "PopupBlocking:GetBlockedPopupList": { - let popupData = []; - let length = this.popupData ? this.popupData.length : 0; - - // Limit 15 popup URLs to be reported through the UI - length = Math.min(length, 15); - - for (let i = 0; i < length; i++) { - let popupWindowURIspec = this.popupData[i].popupWindowURIspec; - - if (popupWindowURIspec == global.content.location.href) { - popupWindowURIspec = "<self>"; - } else { - // Limit 500 chars to be sent because the URI will be cropped - // by the UI anyway, and data: URIs can be significantly larger. - popupWindowURIspec = popupWindowURIspec.substring(0, 500); - } - - popupData.push({popupWindowURIspec}); - } - - sendAsyncMessage("PopupBlocking:ReplyGetBlockedPopupList", {popupData}); - break; - } - } - }, - - handleEvent(ev) { - switch (ev.type) { - case "DOMPopupBlocked": - return this.onPopupBlocked(ev); - case "pageshow": - return this._removeIrrelevantPopupData(); - case "pagehide": - return this._removeIrrelevantPopupData(ev.target); - } - return undefined; - }, - - onPopupBlocked(ev) { - if (!this.popupData) { - this.popupData = []; - this.popupDataInternal = []; - } - - let obj = { - popupWindowURIspec: ev.popupWindowURI ? ev.popupWindowURI.spec : "about:blank", - popupWindowFeatures: ev.popupWindowFeatures, - popupWindowName: ev.popupWindowName - }; - - let internals = { - requestingWindow: ev.requestingWindow, - requestingDocument: ev.requestingWindow.document, - }; - - this.popupData.push(obj); - this.popupDataInternal.push(internals); - this.updateBlockedPopups(true); - }, - - _removeIrrelevantPopupData(removedDoc = null) { - if (this.popupData) { - let i = 0; - let oldLength = this.popupData.length; - while (i < this.popupData.length) { - let {requestingWindow, requestingDocument} = this.popupDataInternal[i]; - // Filter out irrelevant reports. - if (requestingWindow && requestingWindow.document == requestingDocument && - requestingDocument != removedDoc) { - i++; - } else { - this.popupData.splice(i, 1); - this.popupDataInternal.splice(i, 1); - } - } - if (this.popupData.length == 0) { - this.popupData = null; - this.popupDataInternal = null; - } - if (!this.popupData || oldLength > this.popupData.length) { - this.updateBlockedPopups(false); - } - } - }, - - updateBlockedPopups(freshPopup) { - sendAsyncMessage("PopupBlocking:UpdateBlockedPopups", - { - count: this.popupData ? this.popupData.length : 0, - freshPopup - }); - }, -}; -PopupBlocking.init(); - -var Printing = { - MESSAGES: [ - "Printing:Preview:Enter", - "Printing:Preview:Exit", - "Printing:Preview:Navigate", - "Printing:Preview:ParseDocument", - "Printing:Print", - ], - - init() { - this.MESSAGES.forEach(msgName => addMessageListener(msgName, this)); - addEventListener("PrintingError", this, true); - addEventListener("printPreviewUpdate", this, true); - }, - - handleEvent(event) { - return PrintingContent.handleEvent(global, event); - }, - - receiveMessage(message) { - return PrintingContent.receiveMessage(global, message); - }, -}; -Printing.init(); - -function SwitchDocumentDirection(aWindow) { - // document.dir can also be "auto", in which case it won't change - if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") { - aWindow.document.dir = "rtl"; - } else if (aWindow.document.dir == "rtl") { - aWindow.document.dir = "ltr"; - } - for (let run = 0; run < aWindow.frames.length; run++) { - SwitchDocumentDirection(aWindow.frames[run]); } } - -addMessageListener("SwitchDocumentDirection", () => { - SwitchDocumentDirection(content.window); -}); - -var FindBar = { - /* Please keep in sync with toolkit/content/widgets/findbar.xml */ - FIND_NORMAL: 0, - FIND_TYPEAHEAD: 1, - FIND_LINKS: 2, - - _findMode: 0, - - /** - * _findKey and _findModifiers are used to determine whether a keypress - * is a user attempting to use the find shortcut, after which we'll - * route keypresses to the parent until we know the findbar has focus - * there. To do this, we need shortcut data from the parent. - */ - _findKey: null, - _findModifiers: null, - - init() { - addMessageListener("Findbar:UpdateState", this); - Services.els.addSystemEventListener(global, "keypress", this, false); - Services.els.addSystemEventListener(global, "mouseup", this, false); - this._initShortcutData(); - }, - - receiveMessage(msg) { - switch (msg.name) { - case "Findbar:UpdateState": - this._findMode = msg.data.findMode; - this._quickFindTimeout = msg.data.hasQuickFindTimeout; - if (msg.data.isOpenAndFocused) { - this._keepPassingUntilToldOtherwise = false; - } - break; - case "Findbar:ShortcutData": - // Set us up to never need this again for the lifetime of this process, - // and remove the listener. - Services.cpmm.initialProcessData.findBarShortcutData = msg.data; - Services.cpmm.removeMessageListener("Findbar:ShortcutData", this); - this._initShortcutData(msg.data); - break; - } - }, - - handleEvent(event) { - switch (event.type) { - case "keypress": - this._onKeypress(event); - break; - case "mouseup": - this._onMouseup(event); - break; - } - }, - - /** - * Use initial process data for find key/modifier data if we have it. - * Otherwise, add a listener so we get the data when the parent process has - * it. - */ - _initShortcutData(data = Services.cpmm.initialProcessData.findBarShortcutData) { - if (data) { - this._findKey = data.key; - this._findModifiers = data.modifiers; - } else { - Services.cpmm.addMessageListener("Findbar:ShortcutData", this); - } - }, - - /** - * Check whether this key event will start the findbar in the parent, - * in which case we should pass any further key events to the parent to avoid - * them being lost. - * @param aEvent the key event to check. - */ - _eventMatchesFindShortcut(aEvent) { - let modifiers = this._findModifiers; - if (!modifiers) { - return false; - } - return aEvent.ctrlKey == modifiers.ctrlKey && aEvent.altKey == modifiers.altKey && - aEvent.shiftKey == modifiers.shiftKey && aEvent.metaKey == modifiers.metaKey && - aEvent.key == this._findKey; - }, - - /** - * Returns whether FAYT can be used for the given event in - * the current content state. - */ - _canAndShouldFastFind() { - let should = false; - let can = BrowserUtils.canFastFind(content); - if (can) { - // XXXgijs: why all these shenanigans? Why not use the event's target? - let focusedWindow = {}; - let elt = Services.focus.getFocusedElementForWindow(content, true, focusedWindow); - let win = focusedWindow.value; - should = BrowserUtils.shouldFastFind(elt, win); - } - return { can, should }; - }, - - _onKeypress(event) { - const FAYT_LINKS_KEY = "'"; - const FAYT_TEXT_KEY = "/"; - if (this._eventMatchesFindShortcut(event)) { - this._keepPassingUntilToldOtherwise = true; - } - // Useless keys: - if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented) { - return; - } - - // Check the focused element etc. - let fastFind = this._canAndShouldFastFind(); - - // Can we even use find in this page at all? - if (!fastFind.can) { - return; - } - if (this._keepPassingUntilToldOtherwise) { - this._passKeyToParent(event); - return; - } - if (!fastFind.should) { - return; - } - - let charCode = event.charCode; - // If the find bar is open and quick find is on, send the key to the parent. - if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) { - if (!charCode) - return; - this._passKeyToParent(event); - } else { - let key = charCode ? String.fromCharCode(charCode) : null; - let manualstartFAYT = (key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY); - let autostartFAYT = !manualstartFAYT && RemoteFinder._findAsYouType && key && key != " "; - if (manualstartFAYT || autostartFAYT) { - let mode = (key == FAYT_LINKS_KEY || (autostartFAYT && RemoteFinder._typeAheadLinksOnly)) ? - this.FIND_LINKS : this.FIND_TYPEAHEAD; - // Set _findMode immediately (without waiting for child->parent->child roundtrip) - // to ensure we pass any further keypresses, too. - this._findMode = mode; - this._passKeyToParent(event); - } - } - }, - - _passKeyToParent(event) { - event.preventDefault(); - // These are the properties required to dispatch another 'real' event - // to the findbar in the parent in _dispatchKeypressEvent in findbar.xml . - // If you make changes here, verify that that method can still do its job. - const kRequiredProps = [ - "type", "bubbles", "cancelable", "ctrlKey", "altKey", "shiftKey", - "metaKey", "keyCode", "charCode", - ]; - let fakeEvent = {}; - for (let prop of kRequiredProps) { - fakeEvent[prop] = event[prop]; - } - sendAsyncMessage("Findbar:Keypress", fakeEvent); - }, - - _onMouseup(event) { - if (this._findMode != this.FIND_NORMAL) - sendAsyncMessage("Findbar:Mouseup"); - }, -}; -FindBar.init(); - -let WebChannelMessageToChromeListener = { - // Preference containing the list (space separated) of origins that are - // allowed to send non-string values through a WebChannel, mainly for - // backwards compatability. See bug 1238128 for more information. - URL_WHITELIST_PREF: "webchannel.allowObject.urlWhitelist", - - // Cached list of whitelisted principals, we avoid constructing this if the - // value in `_lastWhitelistValue` hasn't changed since we constructed it last. - _cachedWhitelist: [], - _lastWhitelistValue: "", - - init() { - addEventListener("WebChannelMessageToChrome", e => { - this._onMessageToChrome(e); - }, true, true); - }, - - _getWhitelistedPrincipals() { - let whitelist = Services.prefs.getCharPref(this.URL_WHITELIST_PREF); - if (whitelist != this._lastWhitelistValue) { - let urls = whitelist.split(/\s+/); - this._cachedWhitelist = urls.map(origin => - Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin)); - } - return this._cachedWhitelist; - }, - - _onMessageToChrome(e) { - // If target is window then we want the document principal, otherwise fallback to target itself. - let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal; - - if (e.detail) { - if (typeof e.detail != "string") { - // Check if the principal is one of the ones that's allowed to send - // non-string values for e.detail. They're whitelisted by site origin, - // so we compare on originNoSuffix in order to avoid other origin attributes - // that are not relevant here, such as containers or private browsing. - let objectsAllowed = this._getWhitelistedPrincipals().some(whitelisted => - principal.originNoSuffix == whitelisted.originNoSuffix); - if (!objectsAllowed) { - Cu.reportError("WebChannelMessageToChrome sent with an object from a non-whitelisted principal"); - return; - } - } - sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal); - } else { - Cu.reportError("WebChannel message failed. No message detail."); - } - } -}; - -WebChannelMessageToChromeListener.init(); - -// This should be kept in sync with /browser/base/content.js. -// Add message listener for "WebChannelMessageToContent" messages from chrome scripts. -addMessageListener("WebChannelMessageToContent", function(e) { - if (e.data) { - // e.objects.eventTarget will be defined if sending a response to - // a WebChannelMessageToChrome event. An unsolicited send - // may not have an eventTarget defined, in this case send to the - // main content window. - let eventTarget = e.objects.eventTarget || content; - - // Use nodePrincipal if available, otherwise fallback to document principal. - let targetPrincipal = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget.document.nodePrincipal : eventTarget.nodePrincipal; - - if (e.principal.subsumes(targetPrincipal)) { - // If eventTarget is a window, use it as the targetWindow, otherwise - // find the window that owns the eventTarget. - let targetWindow = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget : eventTarget.ownerGlobal; - - eventTarget.dispatchEvent(new targetWindow.CustomEvent("WebChannelMessageToContent", { - detail: Cu.cloneInto({ - id: e.data.id, - message: e.data.message, - }, targetWindow), - })); - } else { - Cu.reportError("WebChannel message failed. Principal mismatch."); - } - } else { - Cu.reportError("WebChannel message failed. No message data."); - } -}); - -var AudioPlaybackListener = { - QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]), - - init() { - Services.obs.addObserver(this, "audio-playback"); - - addMessageListener("AudioPlayback", this); - addEventListener("unload", () => { - AudioPlaybackListener.uninit(); - }); - }, - - uninit() { - Services.obs.removeObserver(this, "audio-playback"); - - removeMessageListener("AudioPlayback", this); - }, - - handleMediaControlMessage(msg) { - let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let suspendTypes = Ci.nsISuspendedTypes; - switch (msg) { - case "mute": - utils.audioMuted = true; - break; - case "unmute": - utils.audioMuted = false; - break; - case "lostAudioFocus": - utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE; - break; - case "lostAudioFocusTransiently": - utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE; - break; - case "gainAudioFocus": - utils.mediaSuspend = suspendTypes.NONE_SUSPENDED; - break; - case "mediaControlPaused": - utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE; - break; - case "mediaControlStopped": - utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE; - break; - case "resumeMedia": - utils.mediaSuspend = suspendTypes.NONE_SUSPENDED; - break; - default: - dump("Error : wrong media control msg!\n"); - break; - } - }, - - observe(subject, topic, data) { - if (topic === "audio-playback") { - if (subject && subject.top == global.content) { - let name = "AudioPlayback:"; - if (data === "activeMediaBlockStart") { - name += "ActiveMediaBlockStart"; - } else if (data === "activeMediaBlockStop") { - name += "ActiveMediaBlockStop"; - } else { - name += (data === "active") ? "Start" : "Stop"; - } - sendAsyncMessage(name); - } - } - }, - - receiveMessage(msg) { - if (msg.name == "AudioPlayback") { - this.handleMediaControlMessage(msg.data.type); - } - }, -}; -AudioPlaybackListener.init(); - -var UnselectedTabHoverObserver = { - init() { - addMessageListener("Browser:UnselectedTabHover", this); - addEventListener("UnselectedTabHover:Enable", this); - addEventListener("UnselectedTabHover:Disable", this); - }, - receiveMessage(message) { - Services.obs.notifyObservers(content.window, "unselected-tab-hover", - message.data.hovered); - }, - handleEvent(event) { - sendAsyncMessage("UnselectedTabHover:Toggle", - { enable: event.type == "UnselectedTabHover:Enable" }); - } -}; -UnselectedTabHoverObserver.init(); - -addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() { - let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory; - if (!sessionHistory) { - return; - } - - // place the entry at current index at the end of the history list, so it won't get removed - if (sessionHistory.index < sessionHistory.count - 1) { - let legacy = sessionHistory.legacySHistory; - legacy.QueryInterface(Ci.nsISHistoryInternal); - let indexEntry = legacy.getEntryAtIndex(sessionHistory.index, false); - indexEntry.QueryInterface(Ci.nsISHEntry); - legacy.addEntry(indexEntry, true); - } - - let purge = sessionHistory.count; - if (global.content.location.href != "about:blank") { - --purge; // Don't remove the page the user's staring at from shistory - } - - if (purge > 0) { - sessionHistory.legacySHistory.PurgeHistory(purge); - } -}); - -addMessageListener("ViewSource:GetSelection", SelectionSourceContent); - -addEventListener("MozApplicationManifest", function(e) { - let doc = e.target; - let info = { - uri: doc.documentURI, - characterSet: doc.characterSet, - manifest: doc.documentElement.getAttribute("manifest"), - principal: doc.nodePrincipal, - }; - sendAsyncMessage("MozApplicationManifest", info); -}, false); - -let AutoCompletePopup = { - QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompletePopup]), - - _connected: false, - - MESSAGES: [ - "FormAutoComplete:HandleEnter", - "FormAutoComplete:PopupClosed", - "FormAutoComplete:PopupOpened", - "FormAutoComplete:RequestFocus", - ], - - init() { - addEventListener("unload", this); - addEventListener("DOMContentLoaded", this); - // WebExtension browserAction is preloaded and does not receive DCL, wait - // on pageshow so we can hookup the formfill controller. - addEventListener("pageshow", this, true); - - for (let messageName of this.MESSAGES) { - addMessageListener(messageName, this); - } - - this._input = null; - this._popupOpen = false; - }, - - destroy() { - if (this._connected) { - let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"] - .getService(Ci.nsIFormFillController); - controller.detachFromBrowser(docShell); - this._connected = false; - } - - removeEventListener("pageshow", this); - removeEventListener("unload", this); - removeEventListener("DOMContentLoaded", this); - - for (let messageName of this.MESSAGES) { - removeMessageListener(messageName, this); - } - }, - - connect() { - if (this._connected) { - return; - } - // We need to wait for a content viewer to be available - // before we can attach our AutoCompletePopup handler, - // since nsFormFillController assumes one will exist - // when we call attachToBrowser. - - // Hook up the form fill autocomplete controller. - let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"] - .getService(Ci.nsIFormFillController); - controller.attachToBrowser(docShell, - this.QueryInterface(Ci.nsIAutoCompletePopup)); - this._connected = true; - }, - - handleEvent(event) { - switch (event.type) { - case "pageshow": { - removeEventListener("pageshow", this); - this.connect(); - break; - } - - case "DOMContentLoaded": { - removeEventListener("DOMContentLoaded", this); - this.connect(); - break; - } - - case "unload": { - this.destroy(); - break; - } - } - }, - - receiveMessage(message) { - switch (message.name) { - case "FormAutoComplete:HandleEnter": { - this.selectedIndex = message.data.selectedIndex; - - let controller = Cc["@mozilla.org/autocomplete/controller;1"] - .getService(Ci.nsIAutoCompleteController); - controller.handleEnter(message.data.isPopupSelection); - break; - } - - case "FormAutoComplete:PopupClosed": { - this._popupOpen = false; - break; - } - - case "FormAutoComplete:PopupOpened": { - this._popupOpen = true; - break; - } - - case "FormAutoComplete:RequestFocus": { - if (this._input) { - this._input.focus(); - } - break; - } - } - }, - - get input() { return this._input; }, - get overrideValue() { return null; }, - set selectedIndex(index) { - sendAsyncMessage("FormAutoComplete:SetSelectedIndex", { index }); - }, - get selectedIndex() { - // selectedIndex getter must be synchronous because we need the - // correct value when the controller is in controller::HandleEnter. - // We can't easily just let the parent inform us the new value every - // time it changes because not every action that can change the - // selectedIndex is trivial to catch (e.g. moving the mouse over the - // list). - return sendSyncMessage("FormAutoComplete:GetSelectedIndex", {}); - }, - get popupOpen() { - return this._popupOpen; - }, - - openAutocompletePopup(input, element) { - if (this._popupOpen || !input) { - return; - } - - let rect = BrowserUtils.getElementBoundingScreenRect(element); - let window = element.ownerGlobal; - let dir = window.getComputedStyle(element).direction; - let results = this.getResultsFromController(input); - - sendAsyncMessage("FormAutoComplete:MaybeOpenPopup", - { results, rect, dir }); - this._input = input; - }, - - closePopup() { - // We set this here instead of just waiting for the - // PopupClosed message to do it so that we don't end - // up in a state where the content thinks that a popup - // is open when it isn't (or soon won't be). - this._popupOpen = false; - sendAsyncMessage("FormAutoComplete:ClosePopup", {}); - }, - - invalidate() { - if (this._popupOpen) { - let results = this.getResultsFromController(this._input); - sendAsyncMessage("FormAutoComplete:Invalidate", { results }); - } - }, - - selectBy(reverse, page) { - this._index = sendSyncMessage("FormAutoComplete:SelectBy", { - reverse, - page - }); - }, - - getResultsFromController(inputField) { - let results = []; - - if (!inputField) { - return results; - } - - let controller = inputField.controller; - if (!(controller instanceof Ci.nsIAutoCompleteController)) { - return results; - } - - for (let i = 0; i < controller.matchCount; ++i) { - let result = {}; - result.value = controller.getValueAt(i); - result.label = controller.getLabelAt(i); - result.comment = controller.getCommentAt(i); - result.style = controller.getStyleAt(i); - result.image = controller.getImageAt(i); - results.push(result); - } - - return results; - }, -}; - -AutoCompletePopup.init(); - -/** - * DateTimePickerListener is the communication channel between the input box - * (content) for date/time input types and its picker (chrome). - */ -let DateTimePickerListener = { - /** - * On init, just listen for the event to open the picker, once the picker is - * opened, we'll listen for update and close events. - */ - init() { - addEventListener("MozOpenDateTimePicker", this); - this._inputElement = null; - - addEventListener("unload", () => { - this.uninit(); - }); - }, - - uninit() { - removeEventListener("MozOpenDateTimePicker", this); - this._inputElement = null; - }, - - /** - * Cleanup function called when picker is closed. - */ - close() { - this.removeListeners(); - this._inputElement.setDateTimePickerState(false); - this._inputElement = null; - }, - - /** - * Called after picker is opened to start listening for input box update - * events. - */ - addListeners() { - addEventListener("MozUpdateDateTimePicker", this); - addEventListener("MozCloseDateTimePicker", this); - addEventListener("pagehide", this); - - addMessageListener("FormDateTime:PickerValueChanged", this); - addMessageListener("FormDateTime:PickerClosed", this); - }, - - /** - * Stop listeneing for events when picker is closed. - */ - removeListeners() { - removeEventListener("MozUpdateDateTimePicker", this); - removeEventListener("MozCloseDateTimePicker", this); - removeEventListener("pagehide", this); - - removeMessageListener("FormDateTime:PickerValueChanged", this); - removeMessageListener("FormDateTime:PickerClosed", this); - }, - - /** - * Helper function that returns the CSS direction property of the element. - */ - getComputedDirection(aElement) { - return aElement.ownerGlobal.getComputedStyle(aElement) - .getPropertyValue("direction"); - }, - - /** - * Helper function that returns the rect of the element, which is the position - * relative to the left/top of the content area. - */ - getBoundingContentRect(aElement) { - return BrowserUtils.getElementBoundingRect(aElement); - }, - - getTimePickerPref() { - return Services.prefs.getBoolPref("dom.forms.datetime.timepicker"); - }, - - /** - * nsIMessageListener. - */ - receiveMessage(aMessage) { - switch (aMessage.name) { - case "FormDateTime:PickerClosed": { - this.close(); - break; - } - case "FormDateTime:PickerValueChanged": { - this._inputElement.updateDateTimeInputBox(aMessage.data); - break; - } - default: - break; - } - }, - - /** - * nsIDOMEventListener, for chrome events sent by the input element and other - * DOM events. - */ - handleEvent(aEvent) { - switch (aEvent.type) { - case "MozOpenDateTimePicker": { - // Time picker is disabled when preffed off - if (!(aEvent.originalTarget instanceof content.HTMLInputElement) || - (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())) { - return; - } - - if (this._inputElement) { - // This happens when we're trying to open a picker when another picker - // is still open. We ignore this request to let the first picker - // close gracefully. - return; - } - - this._inputElement = aEvent.originalTarget; - this._inputElement.setDateTimePickerState(true); - this.addListeners(); - - let value = this._inputElement.getDateTimeInputBoxValue(); - sendAsyncMessage("FormDateTime:OpenPicker", { - rect: this.getBoundingContentRect(this._inputElement), - dir: this.getComputedDirection(this._inputElement), - type: this._inputElement.type, - detail: { - // Pass partial value if it's available, otherwise pass input - // element's value. - value: Object.keys(value).length > 0 ? value - : this._inputElement.value, - min: this._inputElement.getMinimum(), - max: this._inputElement.getMaximum(), - step: this._inputElement.getStep(), - stepBase: this._inputElement.getStepBase(), - }, - }); - break; - } - case "MozUpdateDateTimePicker": { - let value = this._inputElement.getDateTimeInputBoxValue(); - value.type = this._inputElement.type; - sendAsyncMessage("FormDateTime:UpdatePicker", { value }); - break; - } - case "MozCloseDateTimePicker": { - sendAsyncMessage("FormDateTime:ClosePicker"); - this.close(); - break; - } - case "pagehide": { - if (this._inputElement && - this._inputElement.ownerDocument == aEvent.target) { - sendAsyncMessage("FormDateTime:ClosePicker"); - this.close(); - } - break; - } - default: - break; - } - }, -}; - -DateTimePickerListener.init(); - -addEventListener("mozshowdropdown", event => { - if (!event.isTrusted) - return; - - if (!SelectContentHelper.open) { - new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this); - } -}); - -addEventListener("mozshowdropdown-sourcetouch", event => { - if (!event.isTrusted) - return; - - if (!SelectContentHelper.open) { - new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this); - } -}); - -let ExtFind = { - init() { - addMessageListener("ext-Finder:CollectResults", this); - addMessageListener("ext-Finder:HighlightResults", this); - addMessageListener("ext-Finder:clearHighlighting", this); - }, - - _findContent: null, - - async receiveMessage(message) { - if (!this._findContent) { - this._findContent = new FindContent(docShell); - } - - let data; - switch (message.name) { - case "ext-Finder:CollectResults": - this.finderInited = true; - data = await this._findContent.findRanges(message.data); - sendAsyncMessage("ext-Finder:CollectResultsFinished", data); - break; - case "ext-Finder:HighlightResults": - data = this._findContent.highlightResults(message.data); - sendAsyncMessage("ext-Finder:HighlightResultsFinished", data); - break; - case "ext-Finder:clearHighlighting": - this._findContent.highlighter.highlight(false); - break; - } - }, -}; - -ExtFind.init();
copy from toolkit/content/browser-content.js copy to toolkit/modules/DateTimePickerContent.jsm --- a/toolkit/content/browser-content.js +++ b/toolkit/modules/DateTimePickerContent.jsm @@ -1,1199 +1,85 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/* eslint-env mozilla/frame-script */ -/* eslint no-unused-vars: ["error", {args: "none"}] */ -/* global sendAsyncMessage */ - ChromeUtils.import("resource://gre/modules/Services.jsm"); -ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); - ChromeUtils.defineModuleGetter(this, "BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"); -ChromeUtils.defineModuleGetter(this, "SelectContentHelper", - "resource://gre/modules/SelectContentHelper.jsm"); -ChromeUtils.defineModuleGetter(this, "FindContent", - "resource://gre/modules/FindContent.jsm"); -ChromeUtils.defineModuleGetter(this, "PrintingContent", - "resource://gre/modules/PrintingContent.jsm"); -ChromeUtils.defineModuleGetter(this, "RemoteFinder", - "resource://gre/modules/RemoteFinder.jsm"); -XPCOMUtils.defineLazyProxy(this, "SelectionSourceContent", - "resource://gre/modules/SelectionSourceContent.jsm"); - -var global = this; - - -// Lazily load the finder code -addMessageListener("Finder:Initialize", function() { - let {RemoteFinderListener} = ChromeUtils.import("resource://gre/modules/RemoteFinder.jsm", {}); - new RemoteFinderListener(global); -}); - -var ClickEventHandler = { - init: function init() { - this._scrollable = null; - this._scrolldir = ""; - this._startX = null; - this._startY = null; - this._screenX = null; - this._screenY = null; - this._lastFrame = null; - this._autoscrollHandledByApz = false; - this._scrollId = null; - this.autoscrollLoop = this.autoscrollLoop.bind(this); - - Services.els.addSystemEventListener(global, "mousedown", this, true); - - addMessageListener("Autoscroll:Stop", this); - }, - - isAutoscrollBlocker(node) { - let mmPaste = Services.prefs.getBoolPref("middlemouse.paste"); - let mmScrollbarPosition = Services.prefs.getBoolPref("middlemouse.scrollbarPosition"); - - while (node) { - if ((node instanceof content.HTMLAnchorElement || node instanceof content.HTMLAreaElement) && - node.hasAttribute("href")) { - return true; - } - - if (mmPaste && (node instanceof content.HTMLInputElement || - node instanceof content.HTMLTextAreaElement)) { - return true; - } - - if (node instanceof content.XULElement && mmScrollbarPosition - && (node.localName == "scrollbar" || node.localName == "scrollcorner")) { - return true; - } - - node = node.parentNode; - } - return false; - }, - - isScrollableElement(aNode) { - if (aNode instanceof content.HTMLElement) { - return !(aNode instanceof content.HTMLSelectElement) || aNode.multiple; - } - - return aNode instanceof content.XULElement; - }, - - getXBLNodes(parent, array) { - let anonNodes = content.document.getAnonymousNodes(parent); - let nodes = Array.from(anonNodes || parent.childNodes || []); - for (let node of nodes) { - if (node.nodeName == "children") { - return true; - } - if (this.getXBLNodes(node, array)) { - array.push(node); - return true; - } - } - return false; - }, - - * parentNodeIterator(aNode) { - while (aNode) { - yield aNode; - - let parent = aNode.parentNode; - if (parent && parent instanceof content.XULElement) { - let anonNodes = content.document.getAnonymousNodes(parent); - if (anonNodes && !Array.from(anonNodes).includes(aNode)) { - // XBL elements are skipped by parentNode property. - // Yield elements between parent and <children> here. - let nodes = []; - this.getXBLNodes(parent, nodes); - for (let node of nodes) { - yield node; - } - } - } - - aNode = parent; - } - }, - - findNearestScrollableElement(aNode) { - // this is a list of overflow property values that allow scrolling - const scrollingAllowed = ["scroll", "auto"]; - - // go upward in the DOM and find any parent element that has a overflow - // area and can therefore be scrolled - this._scrollable = null; - for (let node of this.parentNodeIterator(aNode)) { - // do not use overflow based autoscroll for <html> and <body> - // Elements or non-html/non-xul elements such as svg or Document nodes - // also make sure to skip select elements that are not multiline - if (!this.isScrollableElement(node)) { - continue; - } - - var overflowx = node.ownerGlobal - .getComputedStyle(node) - .getPropertyValue("overflow-x"); - var overflowy = node.ownerGlobal - .getComputedStyle(node) - .getPropertyValue("overflow-y"); - // we already discarded non-multiline selects so allow vertical - // scroll for multiline ones directly without checking for a - // overflow property - var scrollVert = node.scrollTopMax && - (node instanceof content.HTMLSelectElement || - scrollingAllowed.includes(overflowy)); - - // do not allow horizontal scrolling for select elements, it leads - // to visual artifacts and is not the expected behavior anyway - if (!(node instanceof content.HTMLSelectElement) && - node.scrollLeftMin != node.scrollLeftMax && - scrollingAllowed.includes(overflowx)) { - this._scrolldir = scrollVert ? "NSEW" : "EW"; - this._scrollable = node; - break; - } else if (scrollVert) { - this._scrolldir = "NS"; - this._scrollable = node; - break; - } - } - - if (!this._scrollable) { - this._scrollable = aNode.ownerGlobal; - if (this._scrollable.scrollMaxX != this._scrollable.scrollMinX) { - this._scrolldir = this._scrollable.scrollMaxY != - this._scrollable.scrollMinY ? "NSEW" : "EW"; - } else if (this._scrollable.scrollMaxY != this._scrollable.scrollMinY) { - this._scrolldir = "NS"; - } else if (this._scrollable.frameElement) { - this.findNearestScrollableElement(this._scrollable.frameElement); - } else { - this._scrollable = null; // abort scrolling - } - } - }, - - startScroll(event) { - - this.findNearestScrollableElement(event.originalTarget); - - if (!this._scrollable) - return; - - // In some configurations like Print Preview, content.performance - // (which we use below) is null. Autoscrolling is broken in Print - // Preview anyways (see bug 1393494), so just don't start it at all. - if (!content.performance) - return; - - let domUtils = content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let scrollable = this._scrollable; - if (scrollable instanceof Ci.nsIDOMWindow) { - // getViewId() needs an element to operate on. - scrollable = scrollable.document.documentElement; - } - this._scrollId = null; - try { - this._scrollId = domUtils.getViewId(scrollable); - } catch (e) { - // No view ID - leave this._scrollId as null. Receiving side will check. - } - let presShellId = domUtils.getPresShellId(); - let [result] = sendSyncMessage("Autoscroll:Start", - {scrolldir: this._scrolldir, - screenX: event.screenX, - screenY: event.screenY, - scrollId: this._scrollId, - presShellId}); - if (!result.autoscrollEnabled) { - this._scrollable = null; - return; - } - - Services.els.addSystemEventListener(global, "mousemove", this, true); - addEventListener("pagehide", this, true); - - this._ignoreMouseEvents = true; - this._startX = event.screenX; - this._startY = event.screenY; - this._screenX = event.screenX; - this._screenY = event.screenY; - this._scrollErrorX = 0; - this._scrollErrorY = 0; - this._autoscrollHandledByApz = result.usingApz; - - if (!result.usingApz) { - // If the browser didn't hand the autoscroll off to APZ, - // scroll here in the main thread. - this.startMainThreadScroll(); - } else { - // Even if the browser did hand the autoscroll to APZ, - // APZ might reject it in which case it will notify us - // and we need to take over. - Services.obs.addObserver(this, "autoscroll-rejected-by-apz"); - } - }, - - startMainThreadScroll() { - this._lastFrame = content.performance.now(); - content.requestAnimationFrame(this.autoscrollLoop); - }, - - stopScroll() { - if (this._scrollable) { - this._scrollable.mozScrollSnap(); - this._scrollable = null; - - Services.els.removeSystemEventListener(global, "mousemove", this, true); - removeEventListener("pagehide", this, true); - if (this._autoscrollHandledByApz) { - Services.obs.removeObserver(this, "autoscroll-rejected-by-apz"); - } - } - }, - - accelerate(curr, start) { - const speed = 12; - var val = (curr - start) / speed; - - if (val > 1) - return val * Math.sqrt(val) - 1; - if (val < -1) - return val * Math.sqrt(-val) + 1; - return 0; - }, - - roundToZero(num) { - if (num > 0) - return Math.floor(num); - return Math.ceil(num); - }, - - autoscrollLoop(timestamp) { - if (!this._scrollable) { - // Scrolling has been canceled - return; - } - - // avoid long jumps when the browser hangs for more than - // |maxTimeDelta| ms - const maxTimeDelta = 100; - var timeDelta = Math.min(maxTimeDelta, timestamp - this._lastFrame); - // we used to scroll |accelerate()| pixels every 20ms (50fps) - var timeCompensation = timeDelta / 20; - this._lastFrame = timestamp; - - var actualScrollX = 0; - var actualScrollY = 0; - // don't bother scrolling vertically when the scrolldir is only horizontal - // and the other way around - if (this._scrolldir != "EW") { - var y = this.accelerate(this._screenY, this._startY) * timeCompensation; - var desiredScrollY = this._scrollErrorY + y; - actualScrollY = this.roundToZero(desiredScrollY); - this._scrollErrorY = (desiredScrollY - actualScrollY); - } - if (this._scrolldir != "NS") { - var x = this.accelerate(this._screenX, this._startX) * timeCompensation; - var desiredScrollX = this._scrollErrorX + x; - actualScrollX = this.roundToZero(desiredScrollX); - this._scrollErrorX = (desiredScrollX - actualScrollX); - } - - this._scrollable.scrollBy({ - left: actualScrollX, - top: actualScrollY, - behavior: "instant" - }); - - content.requestAnimationFrame(this.autoscrollLoop); - }, - - handleEvent(event) { - if (event.type == "mousemove") { - this._screenX = event.screenX; - this._screenY = event.screenY; - } else if (event.type == "mousedown") { - if (event.isTrusted & - !event.defaultPrevented && - event.button == 1 && - !this._scrollable && - !this.isAutoscrollBlocker(event.originalTarget)) { - this.startScroll(event); - } - } else if (event.type == "pagehide") { - if (this._scrollable) { - var doc = - this._scrollable.ownerDocument || this._scrollable.document; - if (doc == event.target) { - sendAsyncMessage("Autoscroll:Cancel"); - } - } - } - }, - - receiveMessage(msg) { - switch (msg.name) { - case "Autoscroll:Stop": { - this.stopScroll(); - break; - } - } - }, - - observe(subject, topic, data) { - if (topic === "autoscroll-rejected-by-apz") { - // The caller passes in the scroll id via 'data'. - if (data == this._scrollId) { - this._autoscrollHandledByApz = false; - this.startMainThreadScroll(); - Services.obs.removeObserver(this, "autoscroll-rejected-by-apz"); - } - } - }, -}; -ClickEventHandler.init(); - -var PopupBlocking = { - popupData: null, - popupDataInternal: null, - - init() { - addEventListener("DOMPopupBlocked", this, true); - addEventListener("pageshow", this, true); - addEventListener("pagehide", this, true); - - addMessageListener("PopupBlocking:UnblockPopup", this); - addMessageListener("PopupBlocking:GetBlockedPopupList", this); - }, - - receiveMessage(msg) { - switch (msg.name) { - case "PopupBlocking:UnblockPopup": { - let i = msg.data.index; - if (this.popupData && this.popupData[i]) { - let data = this.popupData[i]; - let internals = this.popupDataInternal[i]; - let dwi = internals.requestingWindow; - - // If we have a requesting window and the requesting document is - // still the current document, open the popup. - if (dwi && dwi.document == internals.requestingDocument) { - dwi.open(data.popupWindowURIspec, data.popupWindowName, data.popupWindowFeatures); - } - } - break; - } - - case "PopupBlocking:GetBlockedPopupList": { - let popupData = []; - let length = this.popupData ? this.popupData.length : 0; - - // Limit 15 popup URLs to be reported through the UI - length = Math.min(length, 15); - - for (let i = 0; i < length; i++) { - let popupWindowURIspec = this.popupData[i].popupWindowURIspec; - - if (popupWindowURIspec == global.content.location.href) { - popupWindowURIspec = "<self>"; - } else { - // Limit 500 chars to be sent because the URI will be cropped - // by the UI anyway, and data: URIs can be significantly larger. - popupWindowURIspec = popupWindowURIspec.substring(0, 500); - } - - popupData.push({popupWindowURIspec}); - } - - sendAsyncMessage("PopupBlocking:ReplyGetBlockedPopupList", {popupData}); - break; - } - } - }, - - handleEvent(ev) { - switch (ev.type) { - case "DOMPopupBlocked": - return this.onPopupBlocked(ev); - case "pageshow": - return this._removeIrrelevantPopupData(); - case "pagehide": - return this._removeIrrelevantPopupData(ev.target); - } - return undefined; - }, - - onPopupBlocked(ev) { - if (!this.popupData) { - this.popupData = []; - this.popupDataInternal = []; - } - - let obj = { - popupWindowURIspec: ev.popupWindowURI ? ev.popupWindowURI.spec : "about:blank", - popupWindowFeatures: ev.popupWindowFeatures, - popupWindowName: ev.popupWindowName - }; - - let internals = { - requestingWindow: ev.requestingWindow, - requestingDocument: ev.requestingWindow.document, - }; - - this.popupData.push(obj); - this.popupDataInternal.push(internals); - this.updateBlockedPopups(true); - }, - - _removeIrrelevantPopupData(removedDoc = null) { - if (this.popupData) { - let i = 0; - let oldLength = this.popupData.length; - while (i < this.popupData.length) { - let {requestingWindow, requestingDocument} = this.popupDataInternal[i]; - // Filter out irrelevant reports. - if (requestingWindow && requestingWindow.document == requestingDocument && - requestingDocument != removedDoc) { - i++; - } else { - this.popupData.splice(i, 1); - this.popupDataInternal.splice(i, 1); - } - } - if (this.popupData.length == 0) { - this.popupData = null; - this.popupDataInternal = null; - } - if (!this.popupData || oldLength > this.popupData.length) { - this.updateBlockedPopups(false); - } - } - }, - - updateBlockedPopups(freshPopup) { - sendAsyncMessage("PopupBlocking:UpdateBlockedPopups", - { - count: this.popupData ? this.popupData.length : 0, - freshPopup - }); - }, -}; -PopupBlocking.init(); - -var Printing = { - MESSAGES: [ - "Printing:Preview:Enter", - "Printing:Preview:Exit", - "Printing:Preview:Navigate", - "Printing:Preview:ParseDocument", - "Printing:Print", - ], - - init() { - this.MESSAGES.forEach(msgName => addMessageListener(msgName, this)); - addEventListener("PrintingError", this, true); - addEventListener("printPreviewUpdate", this, true); - }, - - handleEvent(event) { - return PrintingContent.handleEvent(global, event); - }, - - receiveMessage(message) { - return PrintingContent.receiveMessage(global, message); - }, -}; -Printing.init(); - -function SwitchDocumentDirection(aWindow) { - // document.dir can also be "auto", in which case it won't change - if (aWindow.document.dir == "ltr" || aWindow.document.dir == "") { - aWindow.document.dir = "rtl"; - } else if (aWindow.document.dir == "rtl") { - aWindow.document.dir = "ltr"; - } - for (let run = 0; run < aWindow.frames.length; run++) { - SwitchDocumentDirection(aWindow.frames[run]); - } -} - -addMessageListener("SwitchDocumentDirection", () => { - SwitchDocumentDirection(content.window); -}); - -var FindBar = { - /* Please keep in sync with toolkit/content/widgets/findbar.xml */ - FIND_NORMAL: 0, - FIND_TYPEAHEAD: 1, - FIND_LINKS: 2, - - _findMode: 0, - - /** - * _findKey and _findModifiers are used to determine whether a keypress - * is a user attempting to use the find shortcut, after which we'll - * route keypresses to the parent until we know the findbar has focus - * there. To do this, we need shortcut data from the parent. - */ - _findKey: null, - _findModifiers: null, - - init() { - addMessageListener("Findbar:UpdateState", this); - Services.els.addSystemEventListener(global, "keypress", this, false); - Services.els.addSystemEventListener(global, "mouseup", this, false); - this._initShortcutData(); - }, - - receiveMessage(msg) { - switch (msg.name) { - case "Findbar:UpdateState": - this._findMode = msg.data.findMode; - this._quickFindTimeout = msg.data.hasQuickFindTimeout; - if (msg.data.isOpenAndFocused) { - this._keepPassingUntilToldOtherwise = false; - } - break; - case "Findbar:ShortcutData": - // Set us up to never need this again for the lifetime of this process, - // and remove the listener. - Services.cpmm.initialProcessData.findBarShortcutData = msg.data; - Services.cpmm.removeMessageListener("Findbar:ShortcutData", this); - this._initShortcutData(msg.data); - break; - } - }, - - handleEvent(event) { - switch (event.type) { - case "keypress": - this._onKeypress(event); - break; - case "mouseup": - this._onMouseup(event); - break; - } - }, - - /** - * Use initial process data for find key/modifier data if we have it. - * Otherwise, add a listener so we get the data when the parent process has - * it. - */ - _initShortcutData(data = Services.cpmm.initialProcessData.findBarShortcutData) { - if (data) { - this._findKey = data.key; - this._findModifiers = data.modifiers; - } else { - Services.cpmm.addMessageListener("Findbar:ShortcutData", this); - } - }, - - /** - * Check whether this key event will start the findbar in the parent, - * in which case we should pass any further key events to the parent to avoid - * them being lost. - * @param aEvent the key event to check. - */ - _eventMatchesFindShortcut(aEvent) { - let modifiers = this._findModifiers; - if (!modifiers) { - return false; - } - return aEvent.ctrlKey == modifiers.ctrlKey && aEvent.altKey == modifiers.altKey && - aEvent.shiftKey == modifiers.shiftKey && aEvent.metaKey == modifiers.metaKey && - aEvent.key == this._findKey; - }, - - /** - * Returns whether FAYT can be used for the given event in - * the current content state. - */ - _canAndShouldFastFind() { - let should = false; - let can = BrowserUtils.canFastFind(content); - if (can) { - // XXXgijs: why all these shenanigans? Why not use the event's target? - let focusedWindow = {}; - let elt = Services.focus.getFocusedElementForWindow(content, true, focusedWindow); - let win = focusedWindow.value; - should = BrowserUtils.shouldFastFind(elt, win); - } - return { can, should }; - }, - - _onKeypress(event) { - const FAYT_LINKS_KEY = "'"; - const FAYT_TEXT_KEY = "/"; - if (this._eventMatchesFindShortcut(event)) { - this._keepPassingUntilToldOtherwise = true; - } - // Useless keys: - if (event.ctrlKey || event.altKey || event.metaKey || event.defaultPrevented) { - return; - } - - // Check the focused element etc. - let fastFind = this._canAndShouldFastFind(); - - // Can we even use find in this page at all? - if (!fastFind.can) { - return; - } - if (this._keepPassingUntilToldOtherwise) { - this._passKeyToParent(event); - return; - } - if (!fastFind.should) { - return; - } - - let charCode = event.charCode; - // If the find bar is open and quick find is on, send the key to the parent. - if (this._findMode != this.FIND_NORMAL && this._quickFindTimeout) { - if (!charCode) - return; - this._passKeyToParent(event); - } else { - let key = charCode ? String.fromCharCode(charCode) : null; - let manualstartFAYT = (key == FAYT_LINKS_KEY || key == FAYT_TEXT_KEY); - let autostartFAYT = !manualstartFAYT && RemoteFinder._findAsYouType && key && key != " "; - if (manualstartFAYT || autostartFAYT) { - let mode = (key == FAYT_LINKS_KEY || (autostartFAYT && RemoteFinder._typeAheadLinksOnly)) ? - this.FIND_LINKS : this.FIND_TYPEAHEAD; - // Set _findMode immediately (without waiting for child->parent->child roundtrip) - // to ensure we pass any further keypresses, too. - this._findMode = mode; - this._passKeyToParent(event); - } - } - }, - - _passKeyToParent(event) { - event.preventDefault(); - // These are the properties required to dispatch another 'real' event - // to the findbar in the parent in _dispatchKeypressEvent in findbar.xml . - // If you make changes here, verify that that method can still do its job. - const kRequiredProps = [ - "type", "bubbles", "cancelable", "ctrlKey", "altKey", "shiftKey", - "metaKey", "keyCode", "charCode", - ]; - let fakeEvent = {}; - for (let prop of kRequiredProps) { - fakeEvent[prop] = event[prop]; - } - sendAsyncMessage("Findbar:Keypress", fakeEvent); - }, - - _onMouseup(event) { - if (this._findMode != this.FIND_NORMAL) - sendAsyncMessage("Findbar:Mouseup"); - }, -}; -FindBar.init(); - -let WebChannelMessageToChromeListener = { - // Preference containing the list (space separated) of origins that are - // allowed to send non-string values through a WebChannel, mainly for - // backwards compatability. See bug 1238128 for more information. - URL_WHITELIST_PREF: "webchannel.allowObject.urlWhitelist", - - // Cached list of whitelisted principals, we avoid constructing this if the - // value in `_lastWhitelistValue` hasn't changed since we constructed it last. - _cachedWhitelist: [], - _lastWhitelistValue: "", - - init() { - addEventListener("WebChannelMessageToChrome", e => { - this._onMessageToChrome(e); - }, true, true); - }, - - _getWhitelistedPrincipals() { - let whitelist = Services.prefs.getCharPref(this.URL_WHITELIST_PREF); - if (whitelist != this._lastWhitelistValue) { - let urls = whitelist.split(/\s+/); - this._cachedWhitelist = urls.map(origin => - Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(origin)); - } - return this._cachedWhitelist; - }, - - _onMessageToChrome(e) { - // If target is window then we want the document principal, otherwise fallback to target itself. - let principal = e.target.nodePrincipal ? e.target.nodePrincipal : e.target.document.nodePrincipal; - - if (e.detail) { - if (typeof e.detail != "string") { - // Check if the principal is one of the ones that's allowed to send - // non-string values for e.detail. They're whitelisted by site origin, - // so we compare on originNoSuffix in order to avoid other origin attributes - // that are not relevant here, such as containers or private browsing. - let objectsAllowed = this._getWhitelistedPrincipals().some(whitelisted => - principal.originNoSuffix == whitelisted.originNoSuffix); - if (!objectsAllowed) { - Cu.reportError("WebChannelMessageToChrome sent with an object from a non-whitelisted principal"); - return; - } - } - sendAsyncMessage("WebChannelMessageToChrome", e.detail, { eventTarget: e.target }, principal); - } else { - Cu.reportError("WebChannel message failed. No message detail."); - } - } -}; - -WebChannelMessageToChromeListener.init(); - -// This should be kept in sync with /browser/base/content.js. -// Add message listener for "WebChannelMessageToContent" messages from chrome scripts. -addMessageListener("WebChannelMessageToContent", function(e) { - if (e.data) { - // e.objects.eventTarget will be defined if sending a response to - // a WebChannelMessageToChrome event. An unsolicited send - // may not have an eventTarget defined, in this case send to the - // main content window. - let eventTarget = e.objects.eventTarget || content; - - // Use nodePrincipal if available, otherwise fallback to document principal. - let targetPrincipal = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget.document.nodePrincipal : eventTarget.nodePrincipal; - - if (e.principal.subsumes(targetPrincipal)) { - // If eventTarget is a window, use it as the targetWindow, otherwise - // find the window that owns the eventTarget. - let targetWindow = eventTarget instanceof Ci.nsIDOMWindow ? eventTarget : eventTarget.ownerGlobal; - - eventTarget.dispatchEvent(new targetWindow.CustomEvent("WebChannelMessageToContent", { - detail: Cu.cloneInto({ - id: e.data.id, - message: e.data.message, - }, targetWindow), - })); - } else { - Cu.reportError("WebChannel message failed. Principal mismatch."); - } - } else { - Cu.reportError("WebChannel message failed. No message data."); - } -}); - -var AudioPlaybackListener = { - QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]), - - init() { - Services.obs.addObserver(this, "audio-playback"); - - addMessageListener("AudioPlayback", this); - addEventListener("unload", () => { - AudioPlaybackListener.uninit(); - }); - }, - - uninit() { - Services.obs.removeObserver(this, "audio-playback"); - - removeMessageListener("AudioPlayback", this); - }, - - handleMediaControlMessage(msg) { - let utils = global.content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let suspendTypes = Ci.nsISuspendedTypes; - switch (msg) { - case "mute": - utils.audioMuted = true; - break; - case "unmute": - utils.audioMuted = false; - break; - case "lostAudioFocus": - utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE; - break; - case "lostAudioFocusTransiently": - utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE; - break; - case "gainAudioFocus": - utils.mediaSuspend = suspendTypes.NONE_SUSPENDED; - break; - case "mediaControlPaused": - utils.mediaSuspend = suspendTypes.SUSPENDED_PAUSE_DISPOSABLE; - break; - case "mediaControlStopped": - utils.mediaSuspend = suspendTypes.SUSPENDED_STOP_DISPOSABLE; - break; - case "resumeMedia": - utils.mediaSuspend = suspendTypes.NONE_SUSPENDED; - break; - default: - dump("Error : wrong media control msg!\n"); - break; - } - }, - - observe(subject, topic, data) { - if (topic === "audio-playback") { - if (subject && subject.top == global.content) { - let name = "AudioPlayback:"; - if (data === "activeMediaBlockStart") { - name += "ActiveMediaBlockStart"; - } else if (data === "activeMediaBlockStop") { - name += "ActiveMediaBlockStop"; - } else { - name += (data === "active") ? "Start" : "Stop"; - } - sendAsyncMessage(name); - } - } - }, - - receiveMessage(msg) { - if (msg.name == "AudioPlayback") { - this.handleMediaControlMessage(msg.data.type); - } - }, -}; -AudioPlaybackListener.init(); - -var UnselectedTabHoverObserver = { - init() { - addMessageListener("Browser:UnselectedTabHover", this); - addEventListener("UnselectedTabHover:Enable", this); - addEventListener("UnselectedTabHover:Disable", this); - }, - receiveMessage(message) { - Services.obs.notifyObservers(content.window, "unselected-tab-hover", - message.data.hovered); - }, - handleEvent(event) { - sendAsyncMessage("UnselectedTabHover:Toggle", - { enable: event.type == "UnselectedTabHover:Enable" }); - } -}; -UnselectedTabHoverObserver.init(); - -addMessageListener("Browser:PurgeSessionHistory", function BrowserPurgeHistory() { - let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory; - if (!sessionHistory) { - return; - } - - // place the entry at current index at the end of the history list, so it won't get removed - if (sessionHistory.index < sessionHistory.count - 1) { - let legacy = sessionHistory.legacySHistory; - legacy.QueryInterface(Ci.nsISHistoryInternal); - let indexEntry = legacy.getEntryAtIndex(sessionHistory.index, false); - indexEntry.QueryInterface(Ci.nsISHEntry); - legacy.addEntry(indexEntry, true); - } - - let purge = sessionHistory.count; - if (global.content.location.href != "about:blank") { - --purge; // Don't remove the page the user's staring at from shistory - } - - if (purge > 0) { - sessionHistory.legacySHistory.PurgeHistory(purge); - } -}); - -addMessageListener("ViewSource:GetSelection", SelectionSourceContent); - -addEventListener("MozApplicationManifest", function(e) { - let doc = e.target; - let info = { - uri: doc.documentURI, - characterSet: doc.characterSet, - manifest: doc.documentElement.getAttribute("manifest"), - principal: doc.nodePrincipal, - }; - sendAsyncMessage("MozApplicationManifest", info); -}, false); - -let AutoCompletePopup = { - QueryInterface: ChromeUtils.generateQI([Ci.nsIAutoCompletePopup]), - - _connected: false, - - MESSAGES: [ - "FormAutoComplete:HandleEnter", - "FormAutoComplete:PopupClosed", - "FormAutoComplete:PopupOpened", - "FormAutoComplete:RequestFocus", - ], - - init() { - addEventListener("unload", this); - addEventListener("DOMContentLoaded", this); - // WebExtension browserAction is preloaded and does not receive DCL, wait - // on pageshow so we can hookup the formfill controller. - addEventListener("pageshow", this, true); - - for (let messageName of this.MESSAGES) { - addMessageListener(messageName, this); - } - - this._input = null; - this._popupOpen = false; - }, - - destroy() { - if (this._connected) { - let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"] - .getService(Ci.nsIFormFillController); - controller.detachFromBrowser(docShell); - this._connected = false; - } - - removeEventListener("pageshow", this); - removeEventListener("unload", this); - removeEventListener("DOMContentLoaded", this); - - for (let messageName of this.MESSAGES) { - removeMessageListener(messageName, this); - } - }, - - connect() { - if (this._connected) { - return; - } - // We need to wait for a content viewer to be available - // before we can attach our AutoCompletePopup handler, - // since nsFormFillController assumes one will exist - // when we call attachToBrowser. - - // Hook up the form fill autocomplete controller. - let controller = Cc["@mozilla.org/satchel/form-fill-controller;1"] - .getService(Ci.nsIFormFillController); - controller.attachToBrowser(docShell, - this.QueryInterface(Ci.nsIAutoCompletePopup)); - this._connected = true; - }, - - handleEvent(event) { - switch (event.type) { - case "pageshow": { - removeEventListener("pageshow", this); - this.connect(); - break; - } - - case "DOMContentLoaded": { - removeEventListener("DOMContentLoaded", this); - this.connect(); - break; - } - - case "unload": { - this.destroy(); - break; - } - } - }, - - receiveMessage(message) { - switch (message.name) { - case "FormAutoComplete:HandleEnter": { - this.selectedIndex = message.data.selectedIndex; - - let controller = Cc["@mozilla.org/autocomplete/controller;1"] - .getService(Ci.nsIAutoCompleteController); - controller.handleEnter(message.data.isPopupSelection); - break; - } - - case "FormAutoComplete:PopupClosed": { - this._popupOpen = false; - break; - } - - case "FormAutoComplete:PopupOpened": { - this._popupOpen = true; - break; - } - - case "FormAutoComplete:RequestFocus": { - if (this._input) { - this._input.focus(); - } - break; - } - } - }, - - get input() { return this._input; }, - get overrideValue() { return null; }, - set selectedIndex(index) { - sendAsyncMessage("FormAutoComplete:SetSelectedIndex", { index }); - }, - get selectedIndex() { - // selectedIndex getter must be synchronous because we need the - // correct value when the controller is in controller::HandleEnter. - // We can't easily just let the parent inform us the new value every - // time it changes because not every action that can change the - // selectedIndex is trivial to catch (e.g. moving the mouse over the - // list). - return sendSyncMessage("FormAutoComplete:GetSelectedIndex", {}); - }, - get popupOpen() { - return this._popupOpen; - }, - - openAutocompletePopup(input, element) { - if (this._popupOpen || !input) { - return; - } - - let rect = BrowserUtils.getElementBoundingScreenRect(element); - let window = element.ownerGlobal; - let dir = window.getComputedStyle(element).direction; - let results = this.getResultsFromController(input); - - sendAsyncMessage("FormAutoComplete:MaybeOpenPopup", - { results, rect, dir }); - this._input = input; - }, - - closePopup() { - // We set this here instead of just waiting for the - // PopupClosed message to do it so that we don't end - // up in a state where the content thinks that a popup - // is open when it isn't (or soon won't be). - this._popupOpen = false; - sendAsyncMessage("FormAutoComplete:ClosePopup", {}); - }, - - invalidate() { - if (this._popupOpen) { - let results = this.getResultsFromController(this._input); - sendAsyncMessage("FormAutoComplete:Invalidate", { results }); - } - }, - - selectBy(reverse, page) { - this._index = sendSyncMessage("FormAutoComplete:SelectBy", { - reverse, - page - }); - }, - - getResultsFromController(inputField) { - let results = []; - - if (!inputField) { - return results; - } - - let controller = inputField.controller; - if (!(controller instanceof Ci.nsIAutoCompleteController)) { - return results; - } - - for (let i = 0; i < controller.matchCount; ++i) { - let result = {}; - result.value = controller.getValueAt(i); - result.label = controller.getLabelAt(i); - result.comment = controller.getCommentAt(i); - result.style = controller.getStyleAt(i); - result.image = controller.getImageAt(i); - results.push(result); - } - - return results; - }, -}; - -AutoCompletePopup.init(); +var EXPORTED_SYMBOLS = ["DateTimePickerContent"]; /** - * DateTimePickerListener is the communication channel between the input box + * DateTimePickerContent is the communication channel between the input box * (content) for date/time input types and its picker (chrome). */ -let DateTimePickerListener = { +class DateTimePickerContent { /** * On init, just listen for the event to open the picker, once the picker is * opened, we'll listen for update and close events. */ - init() { - addEventListener("MozOpenDateTimePicker", this); + constructor(global) { this._inputElement = null; - - addEventListener("unload", () => { - this.uninit(); - }); - }, - - uninit() { - removeEventListener("MozOpenDateTimePicker", this); - this._inputElement = null; - }, + this._global = global; + } /** * Cleanup function called when picker is closed. */ close() { this.removeListeners(); this._inputElement.setDateTimePickerState(false); this._inputElement = null; - }, + } /** * Called after picker is opened to start listening for input box update * events. */ addListeners() { - addEventListener("MozUpdateDateTimePicker", this); - addEventListener("MozCloseDateTimePicker", this); - addEventListener("pagehide", this); + this._global.addEventListener("MozUpdateDateTimePicker", this); + this._global.addEventListener("MozCloseDateTimePicker", this); + this._global.addEventListener("pagehide", this); - addMessageListener("FormDateTime:PickerValueChanged", this); - addMessageListener("FormDateTime:PickerClosed", this); - }, + this._global.addMessageListener("FormDateTime:PickerValueChanged", this); + this._global.addMessageListener("FormDateTime:PickerClosed", this); + } /** * Stop listeneing for events when picker is closed. */ removeListeners() { - removeEventListener("MozUpdateDateTimePicker", this); - removeEventListener("MozCloseDateTimePicker", this); - removeEventListener("pagehide", this); + this._global.removeEventListener("MozUpdateDateTimePicker", this); + this._global.removeEventListener("MozCloseDateTimePicker", this); + this._global.removeEventListener("pagehide", this); - removeMessageListener("FormDateTime:PickerValueChanged", this); - removeMessageListener("FormDateTime:PickerClosed", this); - }, + this._global.removeMessageListener("FormDateTime:PickerValueChanged", this); + this._global.removeMessageListener("FormDateTime:PickerClosed", this); + } /** * Helper function that returns the CSS direction property of the element. */ getComputedDirection(aElement) { return aElement.ownerGlobal.getComputedStyle(aElement) .getPropertyValue("direction"); - }, + } /** * Helper function that returns the rect of the element, which is the position * relative to the left/top of the content area. */ getBoundingContentRect(aElement) { return BrowserUtils.getElementBoundingRect(aElement); - }, + } getTimePickerPref() { return Services.prefs.getBoolPref("dom.forms.datetime.timepicker"); - }, + } /** * nsIMessageListener. */ receiveMessage(aMessage) { switch (aMessage.name) { case "FormDateTime:PickerClosed": { this.close(); @@ -1201,44 +87,44 @@ let DateTimePickerListener = { } case "FormDateTime:PickerValueChanged": { this._inputElement.updateDateTimeInputBox(aMessage.data); break; } default: break; } - }, + } /** * nsIDOMEventListener, for chrome events sent by the input element and other * DOM events. */ handleEvent(aEvent) { switch (aEvent.type) { case "MozOpenDateTimePicker": { // Time picker is disabled when preffed off - if (!(aEvent.originalTarget instanceof content.HTMLInputElement) || + if (!(aEvent.originalTarget instanceof aEvent.originalTarget.ownerGlobal.HTMLInputElement) || (aEvent.originalTarget.type == "time" && !this.getTimePickerPref())) { return; } if (this._inputElement) { // This happens when we're trying to open a picker when another picker // is still open. We ignore this request to let the first picker // close gracefully. return; } this._inputElement = aEvent.originalTarget; this._inputElement.setDateTimePickerState(true); this.addListeners(); let value = this._inputElement.getDateTimeInputBoxValue(); - sendAsyncMessage("FormDateTime:OpenPicker", { + this._global.sendAsyncMessage("FormDateTime:OpenPicker", { rect: this.getBoundingContentRect(this._inputElement), dir: this.getComputedDirection(this._inputElement), type: this._inputElement.type, detail: { // Pass partial value if it's available, otherwise pass input // element's value. value: Object.keys(value).length > 0 ? value : this._inputElement.value, @@ -1248,83 +134,29 @@ let DateTimePickerListener = { stepBase: this._inputElement.getStepBase(), }, }); break; } case "MozUpdateDateTimePicker": { let value = this._inputElement.getDateTimeInputBoxValue(); value.type = this._inputElement.type; - sendAsyncMessage("FormDateTime:UpdatePicker", { value }); + this._global.sendAsyncMessage("FormDateTime:UpdatePicker", { value }); break; } case "MozCloseDateTimePicker": { - sendAsyncMessage("FormDateTime:ClosePicker"); + this._global.sendAsyncMessage("FormDateTime:ClosePicker"); this.close(); break; } case "pagehide": { if (this._inputElement && this._inputElement.ownerDocument == aEvent.target) { - sendAsyncMessage("FormDateTime:ClosePicker"); + this._global.sendAsyncMessage("FormDateTime:ClosePicker"); this.close(); } break; } default: break; } - }, -}; - -DateTimePickerListener.init(); - -addEventListener("mozshowdropdown", event => { - if (!event.isTrusted) - return; - - if (!SelectContentHelper.open) { - new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this); } -}); - -addEventListener("mozshowdropdown-sourcetouch", event => { - if (!event.isTrusted) - return; - - if (!SelectContentHelper.open) { - new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this); - } -}); - -let ExtFind = { - init() { - addMessageListener("ext-Finder:CollectResults", this); - addMessageListener("ext-Finder:HighlightResults", this); - addMessageListener("ext-Finder:clearHighlighting", this); - }, - - _findContent: null, - - async receiveMessage(message) { - if (!this._findContent) { - this._findContent = new FindContent(docShell); - } - - let data; - switch (message.name) { - case "ext-Finder:CollectResults": - this.finderInited = true; - data = await this._findContent.findRanges(message.data); - sendAsyncMessage("ext-Finder:CollectResultsFinished", data); - break; - case "ext-Finder:HighlightResults": - data = this._findContent.highlightResults(message.data); - sendAsyncMessage("ext-Finder:HighlightResultsFinished", data); - break; - case "ext-Finder:clearHighlighting": - this._findContent.highlighter.highlight(false); - break; - } - }, -}; - -ExtFind.init(); +}
rename from toolkit/modules/DateTimePickerHelper.jsm rename to toolkit/modules/DateTimePickerParent.jsm --- a/toolkit/modules/DateTimePickerHelper.jsm +++ b/toolkit/modules/DateTimePickerParent.jsm @@ -2,34 +2,34 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const DEBUG = false; function debug(aStr) { if (DEBUG) { - dump("-*- DateTimePickerHelper: " + aStr + "\n"); + dump("-*- DateTimePickerParent: " + aStr + "\n"); } } var EXPORTED_SYMBOLS = [ - "DateTimePickerHelper" + "DateTimePickerParent" ]; ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); /* - * DateTimePickerHelper receives message from content side (input box) and - * is reposible for opening, closing and updating the picker. Similary, - * DateTimePickerHelper listens for picker's events and notifies the content + * DateTimePickerParent receives message from content side (input box) and + * is reposible for opening, closing and updating the picker. Similarly, + * DateTimePickerParent listens for picker's events and notifies the content * side (input box) about them. */ -var DateTimePickerHelper = { +var DateTimePickerParent = { picker: null, weakBrowser: null, MESSAGES: [ "FormDateTime:OpenPicker", "FormDateTime:ClosePicker", "FormDateTime:UpdatePicker" ],
--- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -47,29 +47,32 @@ with Files('tests/xpcshell/test_UpdateUt BUG_COMPONENT = ('Toolkit', 'Application Update') with Files('tests/xpcshell/test_client_id.js'): BUG_COMPONENT = ('Toolkit', 'Telemetry') with Files('AsyncPrefs.jsm'): BUG_COMPONENT = ('Core', 'Security: Process Sandboxing') +with Files('AutoScrollController.jsm'): + BUG_COMPONENT = ('Core', 'Panning and Zooming') + with Files('CharsetMenu.jsm'): BUG_COMPONENT = ('Firefox', 'Toolbars and Customization') with Files('ClientID.jsm'): BUG_COMPONENT = ('Toolkit', 'Telemetry') with Files('Color.jsm'): BUG_COMPONENT = ('Toolkit', 'Find Toolbar') with Files('Console.jsm'): BUG_COMPONENT = ('Firefox', 'Developer Tools: Console') -with Files('DateTimePickerHelper.jsm'): +with Files('DateTimePicker*.jsm'): BUG_COMPONENT = ('Core', 'Layout: Form Controls ') with Files('DeferredTask.jsm'): BUG_COMPONENT = ('Toolkit', 'Async Tooling') with Files("E10SUtils.jsm"): BUG_COMPONENT = ("Core", "Security: Process Sandboxing") @@ -175,28 +178,30 @@ EXTRA_JS_MODULES += [ 'addons/WebNavigationContent.js', 'addons/WebNavigationFrames.jsm', 'addons/WebRequest.jsm', 'addons/WebRequestCommon.jsm', 'addons/WebRequestContent.js', 'addons/WebRequestUpload.jsm', 'AppMenuNotifications.jsm', 'AsyncPrefs.jsm', + 'AutoScrollController.jsm', 'Battery.jsm', 'BinarySearch.jsm', 'BrowserUtils.jsm', 'CanonicalJSON.jsm', 'CertUtils.jsm', 'CharsetMenu.jsm', 'ClientID.jsm', 'Color.jsm', 'Console.jsm', 'CreditCard.jsm', 'css-selector.js', - 'DateTimePickerHelper.jsm', + 'DateTimePickerContent.jsm', + 'DateTimePickerParent.jsm', 'DeferredTask.jsm', 'Deprecated.jsm', 'E10SUtils.jsm', 'EventEmitter.jsm', 'FileUtils.jsm', 'Finder.jsm', 'FinderHighlighter.jsm', 'FinderIterator.jsm',