From 96a33978d6ce040bc27f1f03a1716ee8783e8ff1 Mon Sep 17 00:00:00 2001 From: roytam1 Date: Wed, 7 Aug 2024 16:43:22 +0800 Subject: [PATCH] import changes from `dev' branch of rmottola/Arctic-Fox: - Bug 1254888 - Part 1: Add logging macro to dom/presentation. r=schien (aeecfd2c12) - Bug 1254888 - Part 2: Add log to PresentationSessionInfo and Transport. r=schien (8d705bfda0) - Bug 1148307 - Part1, separate object bruilder from nsIPresentationSessionTransport, r=smaug (038cc48549) - Bug 1239242 - support PresentationRequest.startWithDevice(). r=smaug. (8bb527a997) - Bug 1148307 - Part 2, let session transport send DOM string. r=smaug (61ac0e8d64) - Bug 1148307 - Part 3, implement session transport with DataChannel. r=jib. (c4d124c093) - Bug 1226144 - Free sessionId after using it. r=selin (ee0d36f996) - Bug 1148307 - Part 4, use data channel in substitution for TCP session transport (in-process), r=smaug (8954ab54f8) - Bug 1148307 - Part 5, pref off data channel session transport, r=smaug (61c0c17d1f) - some pref. cleanup (21e17660e7) - add some font names and aliases (cb38962246) - remove unused dom.max_child_script_run_time (d214b353d4) - align strange layout.css.scroll-snap.enabled overwrite (f2562a5cc1) - reshuffle some preferences, remove unused (41f586186b) - more reshuffle and cleanup of preferences (0208aa32a3) - Bug 1168891 Part 1 - Refine two functions related to caret positioning. r=mats (86d718d60e) - Bug 1168891 Part 2 - Allow one caret to be dragged across the other caret. r=mats (9276eb7728) - part of Bug 1252802 - Web page scrolls when dragging caret in editable, r=snorp (31dade8b77) - Bug 1235508 - Re-implement fast Phone number selection on long-press, r=TYLin (59b6371d17) - Bug 1249201 Part 1 - Add "scroll" reason to CaretStateChangedEvent. r=smaug (b92ff6cbfc) - Bug 1249201 Part 2 - Show carets continuously when panning or zooming. r=mats,sebastian (ca5c51c479) - Bug 1245246: Add null check for mDocViewerPrint in nsPrintEngine::FirePrintingErrorEvent. r=roc (e9d5b49a3f) - Bug 1025267 - Make some -moz- prefixed pseudo-classes chrome-only. r=bz (238f7a85d4) - Bug 1259889 Part 1 - Add @supports -moz-bool-pref for internal-only style sheets. r=heycam (d716a7b884) - Bug 1237633 - Part 1: Percentages are not allowed in a . r=jdm (52ccffbf86) - Bug 1081362 - Change nsStyleBasicShape pointer to an nsRefPtr, to avoid leak in unexpected case. r=dholbert (2a5cb8ffdd) - Bug 1264317 - Make the basic shape clip-path clipping use nsCSSValue::Array instead of nsCSSValueList. r=dholbert (7aaf39f2d7) - Bug 1247150 - Consistently use StyleSheetHandle::RefPtr* for outparams in nsLayoutStylesheetCache. r=dholbert (ddc85f29f8) - Bug 1251848: Check StyleSheetHandles for being null-flavored before derefing them, in assertions within nsLayoutStylesheetCache::InvalidateSheet. r=bholley (edb3924075) - Bug 1245260 - Add crashtest; r=hiro (6347e37750) - Bug 460209 - Add crashtest. (97b4786de2) - Bug 474377 - Add crashtest. (516b4e8164) - Bug 1264396 - Don't allow animation of 'display' property; r=heycam (6e94bcb26a) - missing bit of 759568 - Part 1 (fc954f075b) - part of Bug 1037483 replace microdata with microformats (4ff01e11d6) - Bug 1245334 - Make PromiseMessage.jsm ids more meaningful. r=baku (913ac1b9a5) - Bug 1094201 - Implement an Integration.jsm module for low-overhead registration of overrides. r=mak (9982624b90) - Bug 1167663 - Mark nsCSSKeyframeStyleDeclaration/nsCSSPageStyleDeclaration::mRule as MOZ_NON_OWNING_REF. r=dbaron (6d4e9751a1) - Bug 1244992 - Avoid double-counting in various refcounted types related to nsCSSValue. r=heycam. (c830949dd9) - Bug 1262646 - Change the outparams passed to nsStyleUtil::AppendEscapedCSSString from nsString to nsAutoString. r=dholbert (2b0caadf9d) - Bug 1247336 - De-dupe changes in ActiveLayerTracker before treating property as animated. r=roc (c44ed5aee6) - space fix (5e79d245ea) - Bug 1266288 - Track changes to all margin properties for scroll-linked effects. r=mstange (fed6994e4d) - Bug 1259641 - Do not force reflow for all tabs when size mode changed. r=smaug (70847cc6d2) - Bug 1261265 - Fix nsStyleContext::MoveTo flag assertions to allow mismatch on parents if bit is set on child. r=dholbert (3e6b08372e) - Bug 1264837 Part 43 - Remove SVGFEUnstyledLeafFrameBase. r=dholbert (bb55feda77) - Remove mention of old SVG text pref in comment; no bug. (DONTBUILD) (3a618aca18) - Bug 752638, part 1 - Move SVGTextFrame::SetupContextPaint to nsSVGUtils. r=heycam (c125c2903f) - Bug 1258843 - Don't build SVG display items if their visibility is hidden. r=dholbert (150c3b0059) - Bug 1258650. Properly use aExtraMasksTransform when combining masks. r=Bas,a=kwierso (ba5ea1928b) - Bug 1263789 - Stop nsSVGMaskFrameNEON.h from polluting the global namespace. r=dholbert (e2c8544d35) - Bug 1162418 - Try to find a suitable non-zero dimension to use when containing block's inline-size depends on an SVG element which is specified as a percentage of its container. r=jwatt (3eab79c8a4) - Bug 1250143. Account for border/padding on outer elements in GeometryUtils. r=mats (f307820b75) - Bug 1243623. Don't skip unregistering a table part if we have a split table. r=mats (35bb0821c1) - Bug 1203417. Propagate error result from PaintTableFrame. r=seth (866e47b3e4) - Bug 1209780. Propagate the use of MOZ_MUST_USE DrawResult in nsTablePainter. r=seth (851618d06c) - var-const (29d5e9f859) - Bug 1209780. Propagate the use of MOZ_MUST_USE DrawResult in nsTreeBodyFrame::PaintText. r=seth (1ce563ea18) - Bug 1203626 - remove the unused argument from nsTreeBodyFrame::GetTwistyRect. r=mattwoodrow (03293f52b5) - Bug 1218041, part 1: Give nsTreeBodyFrame::PaintImage a fallback codepath for painting SVG images with no explicit height or width. r=seth (b6fd3a39f7) - Bug 1218041, part 2: add reftests for SVG-image rendering. (no review) (90231e0bfa) - Bug 1224736: When image size lookup fails in nsTreeBodyFrame::PaintImage, only fall back to use the full destRect if we've got a VectorImage. r=tn (dd7d7667ca) - Bug 1156108 - Make nsTreeColumns::mFirstColumn an nsRefPtr; r=roc (f6888480bc) - Bug 1255069 - use UniquePtr for storage in nsTreeContentView; r=dholbert (598256735f) - Bug 1181560 - ensure previous menus get closed when opening new ones, r=Enn (2c88f3452a) - Bug 1192655 - Make menubar not react to events when it is not visible. r=enn (2bbcbc81a2) - Bug 1197913 - Keep the last hovered item highlighted after moving the cursor outside the + + Test for data channel as session transport in Presentation API + + + + +Test for data channel as session transport in Presentation API + +
+
+
+ + diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html b/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html new file mode 100644 index 0000000000..dbf724362e --- /dev/null +++ b/dom/presentation/tests/mochitest/test_presentation_dc_receiver.html @@ -0,0 +1,136 @@ + + + + + + Test for B2G PresentationConnection API at receiver side + + + + +Test for B2G PresentationConnection API at receiver side +

+ +

+
+
+
diff --git a/dom/presentation/tests/mochitest/test_presentation_dc_sender.html b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html
new file mode 100644
index 0000000000..2936b5891c
--- /dev/null
+++ b/dom/presentation/tests/mochitest/test_presentation_dc_sender.html
@@ -0,0 +1,208 @@
+
+
+
+
+  
+  Test for B2G Presentation API at sender side
+  
+  
+
+
+Test for B2G Presentation API at sender side
+
+
+
diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html b/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html
new file mode 100644
index 0000000000..7a7ddf298d
--- /dev/null
+++ b/dom/presentation/tests/mochitest/test_presentation_sender_startWithDevice.html
@@ -0,0 +1,172 @@
+
+
+
+
+  
+  Test startWithDevice for B2G Presentation API at sender side
+  
+  
+
+
+Test startWithDevice for B2G Presentation API at sender side
+
+
+
diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html
similarity index 96%
rename from dom/presentation/tests/mochitest/test_presentation_receiver.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html
index d4a3f52da2..76b8921970 100644
--- a/dom/presentation/tests/mochitest/test_presentation_receiver.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver.html
@@ -116,15 +116,13 @@ function runTests() {
   then(testIncomingSessionRequest);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0]]},
+                                      ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_establish_connection_error.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html
similarity index 95%
rename from dom/presentation/tests/mochitest/test_presentation_receiver_establish_connection_error.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html
index 710f626610..a20a85032f 100644
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_establish_connection_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_error.html
@@ -91,15 +91,13 @@ function runTests() {
   then(testIncomingSessionRequest);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0]]},
+                                      ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_establish_connection_timeout.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html
similarity index 93%
rename from dom/presentation/tests/mochitest/test_presentation_receiver_establish_connection_timeout.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html
index 3e243818e0..034aa270ac 100644
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_establish_connection_timeout.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_establish_connection_timeout.html
@@ -65,15 +65,13 @@ function runTests() {
   then(teardown);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0],
+                                      ["dom.presentation.session_transport.data_channel.enable", false],
                                       ["presentation.receiver.loading.timeout", 10]]},
                             runTests);
 });
diff --git a/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html
similarity index 97%
rename from dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html
index 747fc7e3c2..3f44ad91b9 100644
--- a/dom/presentation/tests/mochitest/test_presentation_receiver_oop.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_receiver_oop.html
@@ -157,7 +157,6 @@ function runTests() {
   then(testIncomingSessionRequest);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
@@ -165,8 +164,7 @@ SpecialPowers.pushPermissions([
   {type: 'browser', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0],
+                                      ["dom.presentation.session_transport.data_channel.enable", false],
                                       ["dom.mozBrowserFramesEnabled", true],
                                       ["dom.ipc.browser_frames.oop_by_default", true]]},
                             runTests);
diff --git a/dom/presentation/tests/mochitest/test_presentation_sender.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html
similarity index 97%
rename from dom/presentation/tests/mochitest/test_presentation_sender.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_sender.html
index 6006fd80a9..6c6a683236 100644
--- a/dom/presentation/tests/mochitest/test_presentation_sender.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender.html
@@ -186,15 +186,13 @@ function runTests() {
   then(teardown);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0]]},
+                                      ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_default_request.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
similarity index 96%
rename from dom/presentation/tests/mochitest/test_presentation_sender_default_request.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
index 70f7c57533..6b8e0f1989 100644
--- a/dom/presentation/tests/mochitest/test_presentation_sender_default_request.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_default_request.html
@@ -134,15 +134,13 @@ function runTests() {
   then(teardown);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0]]},
+                                      ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html
similarity index 96%
rename from dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html
index fc12d64349..2f67b42882 100644
--- a/dom/presentation/tests/mochitest/test_presentation_sender_disconnect.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_disconnect.html
@@ -140,15 +140,13 @@ function runTests() {
   then(teardown);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0]]},
+                                      ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
diff --git a/dom/presentation/tests/mochitest/test_presentation_sender_establish_connection_error.html b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
similarity index 98%
rename from dom/presentation/tests/mochitest/test_presentation_sender_establish_connection_error.html
rename to dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
index 1a1e6b7b8e..6f9e44e96d 100644
--- a/dom/presentation/tests/mochitest/test_presentation_sender_establish_connection_error.html
+++ b/dom/presentation/tests/mochitest/test_presentation_tcp_sender_establish_connection_error.html
@@ -344,15 +344,13 @@ function runTests() {
   then(teardown);
 }
 
-SimpleTest.expectAssertions(0, 5);
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPermissions([
   {type: 'presentation-device-manage', allow: false, context: document},
   {type: 'presentation', allow: true, context: document},
 ], function() {
   SpecialPowers.pushPrefEnv({ 'set': [["dom.presentation.enabled", true],
-                                      ["dom.presentation.test.enabled", true],
-                                      ["dom.presentation.test.stage", 0]]},
+                                      ["dom.presentation.session_transport.data_channel.enable", false]]},
                             runTests);
 });
 
diff --git a/dom/presentation/tests/xpcshell/test_presentation_session_transport.js b/dom/presentation/tests/xpcshell/test_presentation_session_transport.js
index 3b76319547..8e207bc22f 100644
--- a/dom/presentation/tests/xpcshell/test_presentation_session_transport.js
+++ b/dom/presentation/tests/xpcshell/test_presentation_session_transport.js
@@ -16,6 +16,9 @@ var testServer = null;
 var clientTransport = null;
 var serverTransport = null;
 
+var clientBuilder = null;
+var serverBuilder = null;
+
 const clientMessage = "Client Message";
 const serverMessage = "Server Message";
 
@@ -84,6 +87,33 @@ const serverCallback = {
   },
 };
 
+const clientListener = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
+  onSessionTransport(aTransport) {
+    Assert.ok(true, "Client Transport is built.");
+    clientTransport = aTransport;
+    clientTransport.callback = clientCallback;
+
+    if (serverTransport) {
+      run_next_test();
+    }
+  }
+}
+
+const serverListener = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPresentationSessionTransportBuilderListener]),
+  onSessionTransport(aTransport) {
+    Assert.ok(true, "Server Transport is built.");
+    serverTransport = aTransport;
+    serverTransport.callback = serverCallback;
+    serverTransport.enableDataNotification();
+
+    if (clientTransport) {
+      run_next_test();
+    }
+  }
+}
+
 function TestServer() {
   this.serverSocket = ServerSocket(-1, true, -1);
   this.serverSocket.asyncListen(this)
@@ -92,10 +122,9 @@ function TestServer() {
 TestServer.prototype = {
   onSocketAccepted: function(aSocket, aTransport) {
     print("Test server gets a client connection.");
-    serverTransport = Cc["@mozilla.org/presentation/presentationsessiontransport;1"]
-                        .createInstance(Ci.nsIPresentationSessionTransport);
-    serverTransport.initWithSocketTransport(aTransport, serverCallback);
-    serverTransport.enableDataNotification();
+    serverBuilder = Cc["@mozilla.org/presentation/presentationtcpsessiontransport;1"]
+                      .createInstance(Ci.nsIPresentationTCPSessionTransportBuilder);
+    serverBuilder.buildTCPSenderTransport(aTransport, serverListener);
   },
   onStopListening: function(aSocket) {
     print("Test server stops listening.");
@@ -111,9 +140,9 @@ TestServer.prototype = {
 // Set up the transport connection and ensure |notifyTransportReady| triggered
 // at both sides.
 function setup() {
-  clientTransport = Cc["@mozilla.org/presentation/presentationsessiontransport;1"]
-                      .createInstance(Ci.nsIPresentationSessionTransport);
-  clientTransport.initWithChannelDescription(serverChannelDescription, clientCallback);
+  clientBuilder = Cc["@mozilla.org/presentation/presentationtcpsessiontransport;1"]
+                    .createInstance(Ci.nsIPresentationTCPSessionTransportBuilder);
+  clientBuilder.buildTCPReceiverTransport(serverChannelDescription, clientListener);
 }
 
 // Test |selfAddress| attribute of |nsIPresentationSessionTransport|.
@@ -132,19 +161,13 @@ function selfAddress() {
 // Test the client sends a message and then a corresponding notification gets
 // triggered at the server side.
 function clientSendMessage() {
-  var stream = Cc["@mozilla.org/io/string-input-stream;1"]
-                 .createInstance(Ci.nsIStringInputStream);
-  stream.setData(clientMessage, clientMessage.length);
-  clientTransport.send(stream);
+  clientTransport.send(clientMessage);
 }
 
 // Test the server sends a message an then a corresponding notification gets
 // triggered at the client side.
 function serverSendMessage() {
-  var stream = Cc["@mozilla.org/io/string-input-stream;1"]
-                 .createInstance(Ci.nsIStringInputStream);
-  stream.setData(serverMessage, serverMessage.length);
-  serverTransport.send(stream);
+  serverTransport.send(serverMessage);
   // The client enables data notification even after the incoming message has
   // been sent, and should still be able to consume it.
   clientTransport.enableDataNotification();
diff --git a/dom/webidl/CaretStateChangedEvent.webidl b/dom/webidl/CaretStateChangedEvent.webidl
index d2bfd5ae73..39edd4d8f8 100644
--- a/dom/webidl/CaretStateChangedEvent.webidl
+++ b/dom/webidl/CaretStateChangedEvent.webidl
@@ -10,7 +10,8 @@ enum CaretChangedReason {
   "longpressonemptycontent",
   "taponcaret",
   "presscaret",
-  "releasecaret"
+  "releasecaret",
+  "scroll"
 };
 
 dictionary CaretStateChangedEventInit : EventInit {
diff --git a/dom/webidl/PresentationConnection.webidl b/dom/webidl/PresentationConnection.webidl
index beb69d5690..c579960a24 100644
--- a/dom/webidl/PresentationConnection.webidl
+++ b/dom/webidl/PresentationConnection.webidl
@@ -43,7 +43,7 @@ interface PresentationConnection : EventTarget {
    *
    * This function only works when the state is "connected".
    *
-   * TODO bug 1148307 Implement PresentationSessionTransport with DataChannel to
+   * TODO bug 1228474 Implement PresentationSessionTransport with DataChannel to
    * support other binary types.
    */
   [Throws]
diff --git a/dom/webidl/PresentationRequest.webidl b/dom/webidl/PresentationRequest.webidl
index cdd0c61b16..bb893cdcee 100644
--- a/dom/webidl/PresentationRequest.webidl
+++ b/dom/webidl/PresentationRequest.webidl
@@ -43,4 +43,24 @@ interface PresentationRequest : EventTarget {
    * The event is fired for all connections that are created for the controller.
    */
   attribute EventHandler onconnectionavailable;
+
+  /*
+   * A chrome page, or page which has presentation-device-manage permissiongs,
+   * uses startWithDevice() to start a new connection with specified device,
+   * and it will be returned with the promise. UA may show a prompt box with a
+   * list of available devices and ask the user to grant permission, choose a
+   * device, or cancel the operation.
+   *
+   * The promise is resolved when the presenting page is successfully loaded and
+   * the communication channel is established, i.e., the connection state is
+   * "connected".
+   *
+   * The promise may be rejected duo to one of the following reasons:
+   * - "OperationError": Unexpected error occurs.
+   * - "NotFoundError":  No available device.
+   * - "NetworkError":   Failed to establish the control channel or data channel.
+   * - "TimeoutError":   Presenting page takes too long to load.
+   */
+  [CheckAnyPermissions="presentation-device-manage", Throws]
+  Promise startWithDevice(DOMString deviceId);
 };
diff --git a/editor/composer/nsComposeTxtSrvFilter.cpp b/editor/composer/nsComposeTxtSrvFilter.cpp
index 418e0ccaab..ba66bca953 100644
--- a/editor/composer/nsComposeTxtSrvFilter.cpp
+++ b/editor/composer/nsComposeTxtSrvFilter.cpp
@@ -45,6 +45,7 @@ nsComposeTxtSrvFilter::Skip(nsIDOMNode* aNode, bool *_retval)
     } else if (content->IsAnyOfHTMLElements(nsGkAtoms::script,
                                             nsGkAtoms::textarea,
                                             nsGkAtoms::select,
+                                            nsGkAtoms::style,
                                             nsGkAtoms::map)) {
       *_retval = true;
     } else if (content->IsHTMLElement(nsGkAtoms::table)) {
diff --git a/editor/composer/test/chrome.ini b/editor/composer/test/chrome.ini
index d57698e9e5..e22614e29d 100644
--- a/editor/composer/test/chrome.ini
+++ b/editor/composer/test/chrome.ini
@@ -6,3 +6,4 @@ skip-if = buildapp == 'b2g'
 [test_bug434998.xul]
 [test_bug678842.html]
 [test_bug717433.html]
+[test_bug1219928.html]
diff --git a/editor/composer/test/test_bug1219928.html b/editor/composer/test/test_bug1219928.html
new file mode 100644
index 0000000000..7915f61bf2
--- /dev/null
+++ b/editor/composer/test/test_bug1219928.html
@@ -0,0 +1,72 @@
+
+
+
+
+  Test for Bug 1219928
+  
+  
+
+
+Mozilla Bug 1219928
+

+ +
+

And here a missspelled word

+ +
+ +
+
+
+ + diff --git a/embedding/browser/nsIWebBrowserChrome.idl b/embedding/browser/nsIWebBrowserChrome.idl index e7a43150a8..40f03cbe4f 100644 --- a/embedding/browser/nsIWebBrowserChrome.idl +++ b/embedding/browser/nsIWebBrowserChrome.idl @@ -31,8 +31,8 @@ interface nsIWebBrowserChrome : nsISupports /** * The currently loaded WebBrowser. The browser chrome may be * told to set the WebBrowser object to a new object by setting this - * attribute. In this case the implementer is responsible for taking the - * new WebBrowser object and doing any necessary initialization or setup + * attribute. In this case the implementer is responsible for taking the + * new WebBrowser object and doing any necessary initialization or setup * as if it had created the WebBrowser itself. This includes positioning * setting up listeners etc. */ @@ -53,7 +53,7 @@ interface nsIWebBrowserChrome : nsISupports const unsigned long CHROME_SCROLLBARS = 0x00000200; const unsigned long CHROME_TITLEBAR = 0x00000400; const unsigned long CHROME_EXTRA = 0x00000800; - + // createBrowserWindow specific flags const unsigned long CHROME_WITH_SIZE = 0x00001000; const unsigned long CHROME_WITH_POSITION = 0x00002000; @@ -98,12 +98,12 @@ interface nsIWebBrowserChrome : nsISupports // Note: The modal style bit just affects the way the window looks and does // mean it's actually modal. - const unsigned long CHROME_MODAL = 0x20000000; + const unsigned long CHROME_MODAL = 0x20000000; const unsigned long CHROME_OPENAS_DIALOG = 0x40000000; const unsigned long CHROME_OPENAS_CHROME = 0x80000000; - + const unsigned long CHROME_ALL = 0x00000ffe; - + /** * The chrome flags for this browser chrome. The implementation should * reflect the value of this attribute by hiding or showing its chrome @@ -118,20 +118,20 @@ interface nsIWebBrowserChrome : nsISupports void destroyBrowserWindow(); /** - * Tells the chrome to size itself such that the browser will be the + * Tells the chrome to size itself such that the browser will be the * specified size. * @param aCX new width of the browser * @param aCY new height of the browser */ void sizeBrowserTo(in long aCX, in long aCY); - + /** * Shows the window as a modal window. * @return (the function error code) the status value specified by * in exitModalEventLoop. */ void showAsModal(); - + /** * Is the window modal (that is, currently executing a modal loop)? * @return true if it's a modal window @@ -145,4 +145,3 @@ interface nsIWebBrowserChrome : nsISupports */ void exitModalEventLoop(in nsresult aStatus); }; - diff --git a/extensions/cookie/nsCookiePermission.cpp b/extensions/cookie/nsCookiePermission.cpp index f7be1a4170..7dfc943f09 100644 --- a/extensions/cookie/nsCookiePermission.cpp +++ b/extensions/cookie/nsCookiePermission.cpp @@ -45,7 +45,6 @@ static const uint32_t ACCEPT_FOR_N_DAYS = 3; static const bool kDefaultPolicy = true; static const char kCookiesLifetimePolicy[] = "network.cookie.lifetimePolicy"; static const char kCookiesLifetimeDays[] = "network.cookie.lifetime.days"; -static const char kCookiesAlwaysAcceptSession[] = "network.cookie.alwaysAcceptSessionCookies"; static const char kCookiesPrefsMigrated[] = "network.cookie.prefsMigrated"; // obsolete pref names for migration @@ -76,7 +75,6 @@ nsCookiePermission::Init() if (prefBranch) { prefBranch->AddObserver(kCookiesLifetimePolicy, this, false); prefBranch->AddObserver(kCookiesLifetimeDays, this, false); - prefBranch->AddObserver(kCookiesAlwaysAcceptSession, this, false); PrefChanged(prefBranch, nullptr); // migration code for original cookie prefs @@ -112,8 +110,7 @@ nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch, if (PREF_CHANGED(kCookiesLifetimePolicy) && NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimePolicy, &val))) { - if (val != static_cast(ACCEPT_SESSION) && - val != static_cast(ACCEPT_FOR_N_DAYS)) { + if (val != static_cast(ACCEPT_SESSION) && val != static_cast(ACCEPT_FOR_N_DAYS)) { val = ACCEPT_NORMALLY; } mCookiesLifetimePolicy = val; @@ -122,12 +119,7 @@ nsCookiePermission::PrefChanged(nsIPrefBranch *aPrefBranch, if (PREF_CHANGED(kCookiesLifetimeDays) && NS_SUCCEEDED(aPrefBranch->GetIntPref(kCookiesLifetimeDays, &val))) // save cookie lifetime in seconds instead of days - mCookiesLifetimeSec = val * 24 * 60 * 60; - - bool bval; - if (PREF_CHANGED(kCookiesAlwaysAcceptSession) && - NS_SUCCEEDED(aPrefBranch->GetBoolPref(kCookiesAlwaysAcceptSession, &bval))) - mCookiesAlwaysAcceptSession = bval; + mCookiesLifetimeSec = (int64_t)val * 24 * 60 * 60; } NS_IMETHODIMP @@ -235,23 +227,23 @@ nsCookiePermission::CanSetCookie(nsIURI *aURI, break; default: - // The permission manager has nothing to say about this cookie - // so we apply the default prefs to it. + // the permission manager has nothing to say about this cookie - + // so, we apply the default prefs to it. NS_ASSERTION(perm == nsIPermissionManager::UNKNOWN_ACTION, "unknown permission"); - // Now we need to figure out what type of accept policy we're dealing with. - // If we accept cookies normally, just bail and return. + // now we need to figure out what type of accept policy we're dealing with + // if we accept cookies normally, just bail and return if (mCookiesLifetimePolicy == ACCEPT_NORMALLY) { *aResult = true; return NS_OK; } - // Declare this here since it'll be used in all of the remaining cases. + // declare this here since it'll be used in all of the remaining cases int64_t currentTime = PR_Now() / PR_USEC_PER_SEC; int64_t delta = *aExpiry - currentTime; - // We are accepting the cookie, but if it's not a session cookie, - // we may have to limit its lifetime. + // We are accepting the cookie, but, + // if it's not a session cookie, we may have to limit its lifetime. if (!*aIsSession && delta > 0) { if (mCookiesLifetimePolicy == ACCEPT_SESSION) { // limit lifetime to session diff --git a/extensions/cookie/nsCookiePermission.h b/extensions/cookie/nsCookiePermission.h index d64720812f..d683f206ac 100644 --- a/extensions/cookie/nsCookiePermission.h +++ b/extensions/cookie/nsCookiePermission.h @@ -24,7 +24,6 @@ public: nsCookiePermission() : mCookiesLifetimeSec(INT64_MAX) , mCookiesLifetimePolicy(0) // ACCEPT_NORMALLY - , mCookiesAlwaysAcceptSession(false) {} bool Init(); @@ -40,7 +39,6 @@ private: int64_t mCookiesLifetimeSec; // lifetime limit specified in seconds uint8_t mCookiesLifetimePolicy; // pref for how long cookies are stored - bool mCookiesAlwaysAcceptSession; // don't prompt for session cookies }; // {EF565D0A-AB9A-4A13-9160-0644CDFD859A} diff --git a/gfx/2d/JobScheduler.cpp b/gfx/2d/JobScheduler.cpp index fad2f21560..2c687cde01 100644 --- a/gfx/2d/JobScheduler.cpp +++ b/gfx/2d/JobScheduler.cpp @@ -77,6 +77,14 @@ JobScheduler::SubmitJob(Job* aJob) GetQueueForJob(aJob)->SubmitJob(aJob); } +void +JobScheduler::Join(SyncObject* aCompletion) +{ + RefPtr waitForCompletion = new EventObject(); + JobScheduler::SubmitJob(new SetEventJob(waitForCompletion, aCompletion)); + waitForCompletion->Wait(); +} + MultiThreadedJobQueue* JobScheduler::GetQueueForJob(Job* aJob) { diff --git a/gfx/2d/JobScheduler.h b/gfx/2d/JobScheduler.h index 55dae4e6b7..4838429042 100644 --- a/gfx/2d/JobScheduler.h +++ b/gfx/2d/JobScheduler.h @@ -71,6 +71,12 @@ public: /// The caller looses ownership of the task buffer. static void SubmitJob(Job* aJobs); + /// Convenience function to block the current thread until a given SyncObject + /// is in the signaled state. + /// + /// The current thread will first try to steal jobs before blocking. + static void Join(SyncObject* aCompletionSync); + /// Process commands until the command buffer needs to block on a sync object, /// completes, yields, or encounters an error. /// diff --git a/gfx/2d/JobScheduler_win32.cpp b/gfx/2d/JobScheduler_win32.cpp index 9c8e925fd3..989965adc3 100644 --- a/gfx/2d/JobScheduler_win32.cpp +++ b/gfx/2d/JobScheduler_win32.cpp @@ -132,9 +132,14 @@ MultiThreadedJobQueue::RegisterThread() void MultiThreadedJobQueue::UnregisterThread() { - CriticalSectionAutoEnter lock(&mSection); + mSection.Enter(); mThreadsCount -= 1; - if (mThreadsCount == 0) { + bool finishShutdown = mThreadsCount == 0; + mSection.Leave(); + + if (finishShutdown) { + // Can't touch mSection or any other member from now on because this object + // may get deleted on the main thread after mShutdownEvent is set. ::SetEvent(mShutdownEvent); } } diff --git a/gfx/2d/Matrix.h b/gfx/2d/Matrix.h index 5813a6185d..b036989da1 100644 --- a/gfx/2d/Matrix.h +++ b/gfx/2d/Matrix.h @@ -548,6 +548,24 @@ public: _33 = 1.0f; _43 = 0.0f; _34 = 0.0f; + // Some matrices, such as those derived from perspective transforms, + // can modify _44 from 1, while leaving the rest of the fourth column + // (_14, _24) at 0. In this case, after resetting the third row and + // third column above, the value of _44 functions only to scale the + // coordinate transform divide by W. The matrix can be converted to + // a true 2D matrix by normalizing out the scaling effect of _44 on + // the remaining components ahead of time. + if (_14 == 0.0f && _24 == 0.0f && + _44 != 1.0f && _44 != 0.0f) { + Float scale = 1.0f / _44; + _11 *= scale; + _12 *= scale; + _21 *= scale; + _22 *= scale; + _41 *= scale; + _42 *= scale; + _44 = 1.0f; + } return *this; } diff --git a/gfx/tests/gtest/TestJobScheduler.cpp b/gfx/tests/gtest/TestJobScheduler.cpp index 8c18ffcaa0..4989fc7a94 100644 --- a/gfx/tests/gtest/TestJobScheduler.cpp +++ b/gfx/tests/gtest/TestJobScheduler.cpp @@ -48,7 +48,7 @@ struct SanityChecker { { MaybeYieldThread(); CriticalSectionAutoEnter lock(&mSection); - ASSERT_EQ(mAdvancements[aJobId], aCmdId-1); + MOZ_RELEASE_ASSERT(mAdvancements[aJobId] == aCmdId-1); mAdvancements[aJobId] = aCmdId; } }; @@ -66,12 +66,12 @@ struct JoinTestSanityCheck : public SanityChecker { { // Job 0 is the special task executed when everything is joined after task 1 if (aCmdId == 0) { - ASSERT_FALSE(mSpecialJobHasRun); + MOZ_RELEASE_ASSERT(!mSpecialJobHasRun); mSpecialJobHasRun = true; for (auto advancement : mAdvancements) { // Because of the synchronization point (beforeFilter), all // task buffers should have run task 1 when task 0 is run. - ASSERT_EQ(advancement, (uint32_t)1); + MOZ_RELEASE_ASSERT(advancement == 1); } } else { // This check does not apply to task 0. @@ -79,7 +79,7 @@ struct JoinTestSanityCheck : public SanityChecker { } if (aCmdId == 2) { - ASSERT_TRUE(mSpecialJobHasRun); + MOZ_RELEASE_ASSERT(mSpecialJobHasRun); } } }; @@ -145,18 +145,12 @@ void TestSchedulerJoin(uint32_t aNumThreads, uint32_t aNumCmdBuffers) } completion->FreezePrerequisites(); - RefPtr waitForCompletion = new EventObject(); - auto evtJob = new SetEventJob(waitForCompletion, completion); - JobScheduler::SubmitJob(evtJob); - - MaybeYieldThread(); - - waitForCompletion->Wait(); + JobScheduler::Join(completion); MaybeYieldThread(); for (auto advancement : check.mAdvancements) { - ASSERT_TRUE(advancement == 2); + EXPECT_TRUE(advancement == 2); } } @@ -205,21 +199,25 @@ void TestSchedulerChain(uint32_t aNumThreads, uint32_t aNumCmdBuffers) } completion->FreezePrerequisites(); - RefPtr waitForCompletion = new EventObject(); - auto evtJob = new SetEventJob(waitForCompletion, completion); - JobScheduler::SubmitJob(evtJob); - - MaybeYieldThread(); - - waitForCompletion->Wait(); + JobScheduler::Join(completion); for (auto advancement : check.mAdvancements) { - ASSERT_TRUE(advancement == numJobs); + EXPECT_TRUE(advancement == numJobs); } } } // namespace test_scheduler +TEST(Moz2D, JobScheduler_Shutdown) { + srand(time(nullptr)); + for (uint32_t threads = 1; threads < 16; ++threads) { + for (uint32_t i = 1; i < 1000; ++i) { + mozilla::gfx::JobScheduler::Init(threads, threads); + mozilla::gfx::JobScheduler::ShutDown(); + } + } +} + TEST(Moz2D, JobScheduler_Join) { srand(time(nullptr)); for (uint32_t threads = 1; threads < 8; ++threads) { diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 77b612ee68..43120d595b 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -361,23 +361,6 @@ gfxAndroidPlatform::RequiresLinearZoom() return gfxPlatform::RequiresLinearZoom(); } -bool -gfxAndroidPlatform::UseAcceleratedSkiaCanvas() -{ - return HaveChoiceOfHWAndSWCanvas() && gfxPlatform::UseAcceleratedSkiaCanvas(); -} - -bool gfxAndroidPlatform::HaveChoiceOfHWAndSWCanvas() -{ -#ifdef MOZ_WIDGET_ANDROID - if (!AndroidBridge::Bridge() || AndroidBridge::Bridge()->GetAPIVersion() < 11) { - // It's slower than software due to not having a compositing fast path - return false; - } -#endif - return gfxPlatform::HaveChoiceOfHWAndSWCanvas(); -} - #ifdef MOZ_WIDGET_GONK class GonkVsyncSource final : public VsyncSource { diff --git a/gfx/thebes/gfxAndroidPlatform.h b/gfx/thebes/gfxAndroidPlatform.h index f5f4b2b2e3..d6ffdf8b43 100644 --- a/gfx/thebes/gfxAndroidPlatform.h +++ b/gfx/thebes/gfxAndroidPlatform.h @@ -66,8 +66,6 @@ public: return true; } - virtual bool HaveChoiceOfHWAndSWCanvas() override; - virtual bool UseAcceleratedSkiaCanvas() override; virtual already_AddRefed CreateHardwareVsyncSource() override; #ifdef MOZ_WIDGET_GONK diff --git a/layout/base/AccessibleCaretManager.cpp b/layout/base/AccessibleCaretManager.cpp index e563e8ec26..d799abf2d3 100644 --- a/layout/base/AccessibleCaretManager.cpp +++ b/layout/base/AccessibleCaretManager.cpp @@ -23,6 +23,9 @@ #include "nsFrameSelection.h" #include "nsGenericHTMLElement.h" #include "nsIHapticFeedback.h" +#ifdef MOZ_WIDGET_ANDROID +#include "nsWindow.h" +#endif namespace mozilla { @@ -69,13 +72,17 @@ AccessibleCaretManager::sSelectionBarEnabled = false; /*static*/ bool AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = false; /*static*/ bool -AccessibleCaretManager::sCaretsExtendedVisibility = false; -/*static*/ bool AccessibleCaretManager::sCaretsAlwaysTilt = false; /*static*/ bool +AccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = true; +/*static*/ bool AccessibleCaretManager::sCaretsScriptUpdates = false; /*static*/ bool +AccessibleCaretManager::sCaretsAllowDraggingAcrossOtherCaret = true; +/*static*/ bool AccessibleCaretManager::sHapticFeedback = false; +/*static*/ bool +AccessibleCaretManager::sExtendSelectionForPhoneNumber = false; AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell) : mPresShell(aPresShell) @@ -95,14 +102,18 @@ AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell) "layout.accessiblecaret.bar.enabled"); Preferences::AddBoolVarCache(&sCaretShownWhenLongTappingOnEmptyContent, "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content"); - Preferences::AddBoolVarCache(&sCaretsExtendedVisibility, - "layout.accessiblecaret.extendedvisibility"); Preferences::AddBoolVarCache(&sCaretsAlwaysTilt, "layout.accessiblecaret.always_tilt"); + Preferences::AddBoolVarCache(&sCaretsAlwaysShowWhenScrolling, + "layout.accessiblecaret.always_show_when_scrolling", true); Preferences::AddBoolVarCache(&sCaretsScriptUpdates, "layout.accessiblecaret.allow_script_change_updates"); + Preferences::AddBoolVarCache(&sCaretsAllowDraggingAcrossOtherCaret, + "layout.accessiblecaret.allow_dragging_across_other_caret", true); Preferences::AddBoolVarCache(&sHapticFeedback, "layout.accessiblecaret.hapticfeedback"); + Preferences::AddBoolVarCache(&sExtendSelectionForPhoneNumber, + "layout.accessiblecaret.extend_selection_for_phone_number"); addedPrefs = true; } } @@ -197,18 +208,6 @@ AccessibleCaretManager::HideCarets() } } -void -AccessibleCaretManager::DoNotShowCarets() -{ - if (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible()) { - AC_LOG("%s", __FUNCTION__); - mFirstCaret->SetAppearance(Appearance::NormalNotShown); - mSecondCaret->SetAppearance(Appearance::NormalNotShown); - DispatchCaretStateChangedEvent(CaretChangedReason::Visibilitychange); - CancelCaretTimeoutTimer(); - } -} - void AccessibleCaretManager::UpdateCarets(UpdateCaretsHint aHint) { @@ -345,10 +344,12 @@ AccessibleCaretManager::UpdateCaretsForSelectionMode(UpdateCaretsHint aHint) AC_LOG("%s: selection: %p", __FUNCTION__, GetSelection()); int32_t startOffset = 0; - nsIFrame* startFrame = FindFirstNodeWithFrame(false, &startOffset); + nsIFrame* startFrame = + GetFrameForFirstRangeStartOrLastRangeEnd(eDirNext, &startOffset); int32_t endOffset = 0; - nsIFrame* endFrame = FindFirstNodeWithFrame(true, &endOffset); + nsIFrame* endFrame = + GetFrameForFirstRangeStartOrLastRangeEnd(eDirPrevious, &endOffset); if (!CompareTreePosition(startFrame, endFrame)) { // XXX: Do we really have to hide carets if this condition isn't satisfied? @@ -620,14 +621,19 @@ AccessibleCaretManager::OnScrollStart() { AC_LOG("%s", __FUNCTION__); - mFirstCaretAppearanceOnScrollStart = mFirstCaret->GetAppearance(); - mSecondCaretAppearanceOnScrollStart = mSecondCaret->GetAppearance(); - - // Hide the carets. (Extended visibility makes them "NormalNotShown"). - if (sCaretsExtendedVisibility) { - DoNotShowCarets(); - } else { + if (!sCaretsAlwaysShowWhenScrolling) { + // Backup the appearance so that we can restore them after the scrolling + // ends. + mFirstCaretAppearanceOnScrollStart = mFirstCaret->GetAppearance(); + mSecondCaretAppearanceOnScrollStart = mSecondCaret->GetAppearance(); HideCarets(); + return; + } + + if (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible()) { + // Dispatch the event only if one of the carets is logically visible like in + // HideCarets(). + DispatchCaretStateChangedEvent(CaretChangedReason::Scroll); } } @@ -638,8 +644,11 @@ AccessibleCaretManager::OnScrollEnd() return; } - mFirstCaret->SetAppearance(mFirstCaretAppearanceOnScrollStart); - mSecondCaret->SetAppearance(mSecondCaretAppearanceOnScrollStart); + if (!sCaretsAlwaysShowWhenScrolling) { + // Restore the appearance which is saved before the scrolling is started. + mFirstCaret->SetAppearance(mFirstCaretAppearanceOnScrollStart); + mSecondCaret->SetAppearance(mSecondCaretAppearanceOnScrollStart); + } if (GetCaretMode() == CaretMode::Cursor) { if (!mFirstCaret->IsLogicallyVisible()) { @@ -818,6 +827,11 @@ AccessibleCaretManager::SelectWord(nsIFrame* aFrame, const nsPoint& aPoint) cons SetSelectionDragState(false); ClearMaintainedSelection(); + // Smart-select phone numbers if possible. + if (sExtendSelectionForPhoneNumber) { + SelectMoreIfPhoneNumber(); + } + return rs; } @@ -828,6 +842,62 @@ AccessibleCaretManager::SetSelectionDragState(bool aState) const if (fs) { fs->SetDragState(aState); } + + // Pin Fennecs DynamicToolbarAnimator in place before/after dragging, + // to avoid co-incident screen scrolling. + #ifdef MOZ_WIDGET_ANDROID + nsIDocument* doc = mPresShell->GetDocument(); + MOZ_ASSERT(doc); + nsIWidget* widget = nsContentUtils::WidgetForDocument(doc); + static_cast(widget)->SetSelectionDragState(aState); + #endif +} + +void +AccessibleCaretManager::SelectMoreIfPhoneNumber() const +{ + SetSelectionDirection(eDirNext); + ExtendPhoneNumberSelection(NS_LITERAL_STRING("forward")); + + SetSelectionDirection(eDirPrevious); + ExtendPhoneNumberSelection(NS_LITERAL_STRING("backward")); +} + +void +AccessibleCaretManager::ExtendPhoneNumberSelection(const nsAString& aDirection) const +{ + nsIDocument* doc = mPresShell->GetDocument(); + + // Extend the phone number selection until we find a boundary. + Selection* selection = GetSelection(); + + while (selection) { + // Save current Focus position, and extend the selection one char. + nsINode* focusNode = selection->GetFocusNode(); + uint32_t focusOffset = selection->FocusOffset(); + selection->Modify(NS_LITERAL_STRING("extend"), + aDirection, + NS_LITERAL_STRING("character")); + + // If the selection didn't change, (can't extend further), we're done. + if (selection->GetFocusNode() == focusNode && + selection->FocusOffset() == focusOffset) { + return; + } + + // If the changed selection isn't a valid phone number, we're done. + nsAutoString selectedText; + selection->Stringify(selectedText); + nsAutoString phoneRegex(NS_LITERAL_STRING("(^\\+)?[0-9\\s,\\-.()*#pw]{1,30}$")); + + if (!nsContentUtils::IsPatternMatching(selectedText, phoneRegex, doc)) { + // Backout the undesired selection extend, (collapse to original + // Anchor, extend to original Focus), before exit. + selection->Collapse(selection->GetAnchorNode(), selection->AnchorOffset()); + selection->Extend(focusNode, focusOffset); + return; + } + } } void @@ -859,41 +929,51 @@ AccessibleCaretManager::FlushLayout() const } nsIFrame* -AccessibleCaretManager::FindFirstNodeWithFrame(bool aBackward, - int32_t* aOutOffset) const +AccessibleCaretManager::GetFrameForFirstRangeStartOrLastRangeEnd( + nsDirection aDirection, int32_t* aOutOffset, nsINode** aOutNode, + int32_t* aOutNodeOffset) const { if (!mPresShell) { return nullptr; } + MOZ_ASSERT(GetCaretMode() == CaretMode::Selection); + + nsRange* range = nullptr; + RefPtr startNode; + RefPtr endNode; + int32_t nodeOffset = 0; + CaretAssociationHint hint; + RefPtr selection = GetSelection(); - if (!selection) { - return nullptr; + bool findInFirstRangeStart = aDirection == eDirNext; + + if (findInFirstRangeStart) { + range = selection->GetRangeAt(0); + startNode = range->GetStartParent(); + endNode = range->GetEndParent(); + nodeOffset = range->StartOffset(); + hint = CARET_ASSOCIATE_AFTER; + } else { + range = selection->GetRangeAt(selection->RangeCount() - 1); + startNode = range->GetEndParent(); + endNode = range->GetStartParent(); + nodeOffset = range->EndOffset(); + hint = CARET_ASSOCIATE_BEFORE; } - RefPtr fs = GetFrameSelection(); - if (!fs) { - return nullptr; - } - - uint32_t rangeCount = selection->RangeCount(); - if (rangeCount <= 0) { - return nullptr; - } - - nsRange* range = selection->GetRangeAt(aBackward ? rangeCount - 1 : 0); - RefPtr startNode = - aBackward ? range->GetEndParent() : range->GetStartParent(); - RefPtr endNode = - aBackward ? range->GetStartParent() : range->GetEndParent(); - int32_t offset = aBackward ? range->EndOffset() : range->StartOffset(); nsCOMPtr startContent = do_QueryInterface(startNode); - CaretAssociationHint hintStart = - aBackward ? CARET_ASSOCIATE_BEFORE : CARET_ASSOCIATE_AFTER; + RefPtr fs = GetFrameSelection(); nsIFrame* startFrame = - fs->GetFrameForNodeOffset(startContent, offset, hintStart, aOutOffset); + fs->GetFrameForNodeOffset(startContent, nodeOffset, hint, aOutOffset); if (startFrame) { + if (aOutNode) { + *aOutNode = startNode.get(); + } + if (aOutNodeOffset) { + *aOutNodeOffset = nodeOffset; + } return startFrame; } @@ -907,7 +987,8 @@ AccessibleCaretManager::FindFirstNodeWithFrame(bool aBackward, startFrame = startContent ? startContent->GetPrimaryFrame() : nullptr; while (!startFrame && startNode != endNode) { - startNode = aBackward ? walker->PreviousNode(err) : walker->NextNode(err); + startNode = findInFirstRangeStart ? walker->NextNode(err) + : walker->PreviousNode(err); if (!startNode) { break; @@ -920,78 +1001,86 @@ AccessibleCaretManager::FindFirstNodeWithFrame(bool aBackward, } bool -AccessibleCaretManager::CompareRangeWithContentOffset(nsIFrame::ContentOffsets& aOffsets) +AccessibleCaretManager::RestrictCaretDraggingOffsets( + nsIFrame::ContentOffsets& aOffsets) { - Selection* selection = GetSelection(); - if (!selection) { + if (!mPresShell) { return false; } - uint32_t rangeCount = selection->RangeCount(); - MOZ_ASSERT(rangeCount > 0); - - int32_t rangeIndex = (mActiveCaret == mFirstCaret.get() ? rangeCount - 1 : 0); - RefPtr range = selection->GetRangeAt(rangeIndex); + MOZ_ASSERT(GetCaretMode() == CaretMode::Selection); + nsDirection dir = mActiveCaret == mFirstCaret.get() ? eDirPrevious : eDirNext; + int32_t offset = 0; nsINode* node = nullptr; - int32_t nodeOffset = 0; - CaretAssociationHint hint; - nsDirection dir; + int32_t contentOffset = 0; + nsIFrame* frame = + GetFrameForFirstRangeStartOrLastRangeEnd(dir, &offset, &node, &contentOffset); - if (mActiveCaret == mFirstCaret.get()) { - // Check previous character of end node offset - node = range->GetEndParent(); - nodeOffset = range->EndOffset(); - hint = CARET_ASSOCIATE_BEFORE; - dir = eDirPrevious; - } else { - // Check next character of start node offset - node = range->GetStartParent(); - nodeOffset = range->StartOffset(); - hint = CARET_ASSOCIATE_AFTER; - dir = eDirNext; + if (!frame) { + return false; } + nsCOMPtr content = do_QueryInterface(node); - RefPtr fs = GetFrameSelection(); - if (!fs) { - return false; - } + // Compare the active caret's new position (aOffsets) to the inactive caret's + // position. + int32_t cmpToInactiveCaretPos = + nsContentUtils::ComparePoints(aOffsets.content, aOffsets.StartOffset(), + content, contentOffset); - int32_t offset = 0; - nsIFrame* theFrame = - fs->GetFrameForNodeOffset(content, nodeOffset, hint, &offset); - - if (!theFrame) { - return false; - } - - // Move one character forward/backward from point and get offset - nsPeekOffsetStruct pos(eSelectCluster, - dir, - offset, - nsPoint(0, 0), - true, - true, //limit on scrolled views - false, - false, - false); - nsresult rv = theFrame->PeekOffset(&pos); + // Move one character (in the direction of dir) from the inactive caret's + // position. This is the limit for the active caret's new position. + nsPeekOffsetStruct limit(eSelectCluster, dir, offset, nsPoint(0, 0), true, true, + false, false, false); + nsresult rv = frame->PeekOffset(&limit); if (NS_FAILED(rv)) { - pos.mResultContent = content; - pos.mContentOffset = nodeOffset; + limit.mResultContent = content; + limit.mContentOffset = contentOffset; } - // Compare with current point - int32_t result = nsContentUtils::ComparePoints(aOffsets.content, - aOffsets.StartOffset(), - pos.mResultContent, - pos.mContentOffset); - if ((mActiveCaret == mFirstCaret.get() && result == 1) || - (mActiveCaret == mSecondCaret.get() && result == -1)) { - aOffsets.content = pos.mResultContent; - aOffsets.offset = pos.mContentOffset; - aOffsets.secondaryOffset = pos.mContentOffset; + // Compare the active caret's new position (aOffsets) to the limit. + int32_t cmpToLimit = + nsContentUtils::ComparePoints(aOffsets.content, aOffsets.StartOffset(), + limit.mResultContent, limit.mContentOffset); + + auto SetOffsetsToLimit = [&aOffsets, &limit] () { + aOffsets.content = limit.mResultContent; + aOffsets.offset = limit.mContentOffset; + aOffsets.secondaryOffset = limit.mContentOffset; + }; + + if (!sCaretsAllowDraggingAcrossOtherCaret) { + if ((mActiveCaret == mFirstCaret.get() && cmpToLimit == 1) || + (mActiveCaret == mSecondCaret.get() && cmpToLimit == -1)) { + // The active caret's position is past the limit, which we don't allow + // here. So set it to the limit, resulting in one character being + // selected. + SetOffsetsToLimit(); + } + } else { + switch (cmpToInactiveCaretPos) { + case 0: + // The active caret's position is the same as the position of the + // inactive caret. So set it to the limit to prevent the selection from + // being collapsed, resulting in one character being selected. + SetOffsetsToLimit(); + break; + case 1: + if (mActiveCaret == mFirstCaret.get()) { + // First caret was moved across the second caret. After making change + // to the selection, the user will drag the second caret. + mActiveCaret = mSecondCaret.get(); + } + break; + case -1: + if (mActiveCaret == mSecondCaret.get()) { + // Second caret was moved across the first caret. After making change + // to the selection, the user will drag the first caret. + mActiveCaret = mFirstCaret.get(); + } + break; + } } return true; @@ -1051,7 +1140,7 @@ AccessibleCaretManager::DragCaretInternal(const nsPoint& aPoint) nsIFrame::ContentOffsets offsets = newFrame->GetContentOffsetsFromPoint(newPoint); - if (!offsets.content) { + if (offsets.IsNull()) { return NS_ERROR_FAILURE; } @@ -1061,7 +1150,7 @@ AccessibleCaretManager::DragCaretInternal(const nsPoint& aPoint) } if (GetCaretMode() == CaretMode::Selection && - !CompareRangeWithContentOffset(offsets)) { + !RestrictCaretDraggingOffsets(offsets)) { return NS_ERROR_FAILURE; } @@ -1154,7 +1243,8 @@ AccessibleCaretManager::AdjustDragBoundary(const nsPoint& aPoint) const } } - if (GetCaretMode() == CaretMode::Selection) { + if (GetCaretMode() == CaretMode::Selection && + !sCaretsAllowDraggingAcrossOtherCaret) { // Bug 1068474: Adjust the Y-coordinate so that the carets won't be in tilt // mode when a caret is being dragged surpass the other caret. // diff --git a/layout/base/AccessibleCaretManager.h b/layout/base/AccessibleCaretManager.h index 9572c8b9c1..8a05c72959 100644 --- a/layout/base/AccessibleCaretManager.h +++ b/layout/base/AccessibleCaretManager.h @@ -135,10 +135,6 @@ protected: // Force hiding all carets regardless of the current selection status. void HideCarets(); - // Force carets to be "present" logically, but not visible. Allows ActionBar - // to stay open when carets visibility is supressed during scroll. - void DoNotShowCarets(); - void UpdateCaretsForCursorMode(UpdateCaretsHint aHint); void UpdateCaretsForSelectionMode(UpdateCaretsHint aHint); @@ -155,12 +151,21 @@ protected: nsresult SelectWord(nsIFrame* aFrame, const nsPoint& aPoint) const; void SetSelectionDragState(bool aState) const; + + // Called to extend a selection if possible that it's a phone number. + void SelectMoreIfPhoneNumber() const; + // Extend the current phone number selection in the requested direction. + void ExtendPhoneNumberSelection(const nsAString& aDirection) const; + void SetSelectionDirection(nsDirection aDir) const; - // If aBackward is false, find the first node from the first range in current - // selection, and return the frame and the offset into that frame. If aBackward - // is true, find the last node from the last range instead. - nsIFrame* FindFirstNodeWithFrame(bool aBackward, int32_t* aOutOffset) const; + // If aDirection is eDirNext, get the frame for the range start in the first + // range from the current selection, and return the offset into that frame as + // well as the range start node and the node offset. Otherwise, get the frame + // and offset for the range end in the last range instead. + nsIFrame* GetFrameForFirstRangeStartOrLastRangeEnd( + nsDirection aDirection, int32_t* aOutOffset, nsINode** aOutNode = nullptr, + int32_t* aOutNodeOffset = nullptr) const; nsresult DragCaretInternal(const nsPoint& aPoint); nsPoint AdjustDragBoundary(const nsPoint& aPoint) const; @@ -179,11 +184,18 @@ protected: // be dragged. Returns the rect relative to aFrame. nsRect GetAllChildFrameRectsUnion(nsIFrame* aFrame) const; - // If we're dragging the first caret, we do not want to drag it over the - // previous character of the second caret. Same as the second caret. So we - // check if content offset exceeds the previous/next character of second/first - // caret base the active caret. - bool CompareRangeWithContentOffset(nsIFrame::ContentOffsets& aOffsets); + // Restrict the active caret's dragging position based on + // sCaretsAllowDraggingAcrossOtherCaret. If the active caret is the first + // caret, the `limit` will be the previous character of the second caret. + // Otherwise, the `limit` will be the next character of the first caret. + // + // @param aOffsets is the new position of the active caret, and it will be set + // to the `limit` when 1) sCaretsAllowDraggingAcrossOtherCaret is false and + // it's being dragged past the limit. 2) sCaretsAllowDraggingAcrossOtherCaret + // is true and the active caret's position is the same as the inactive's + // position. + // @return true if the aOffsets is suitable for changing the selection. + bool RestrictCaretDraggingOffsets(nsIFrame::ContentOffsets& aOffsets); // Timeout in milliseconds to hide the AccessibleCaret under cursor mode while // no one touches it. @@ -272,25 +284,35 @@ protected: // selection bar is always disabled in cursor mode. static bool sSelectionBarEnabled; + // Preference to allow smarter selection of phone numbers, + // when user long presses text to start. + static bool sExtendSelectionForPhoneNumber; + // Preference to show caret in cursor mode when long tapping on an empty // content. This also changes the default update behavior in cursor mode, // which is based on the emptiness of the content, into something more // heuristic. See UpdateCaretsForCursorMode() for the details. static bool sCaretShownWhenLongTappingOnEmptyContent; - // Android specific visibility extensions correct compatibility issues - // with ActionBar visibility during page scroll. - static bool sCaretsExtendedVisibility; - // Preference to make carets always tilt in selection mode. By default, the // carets become tilt only when they are overlapping. static bool sCaretsAlwaysTilt; + // Preference to allow carets always show when scrolling (either panning or + // zooming) the page. When set to false, carets will hide during scrolling, + // and show again after the user lifts the finger off the screen. + static bool sCaretsAlwaysShowWhenScrolling; + // By default, javascript content selection changes closes AccessibleCarets and // UI interactions. Optionally, we can try to maintain the active UI, keeping // carets and ActionBar available. static bool sCaretsScriptUpdates; + // Preference to allow one caret to be dragged across the other caret without + // any limitation. When set to false, one caret cannot be dragged across the + // other one. + static bool sCaretsAllowDraggingAcrossOtherCaret; + // AccessibleCaret pref for haptic feedback behaviour on longPress. static bool sHapticFeedback; }; diff --git a/layout/base/ActiveLayerTracker.cpp b/layout/base/ActiveLayerTracker.cpp index cd071b739a..6ae671f096 100644 --- a/layout/base/ActiveLayerTracker.cpp +++ b/layout/base/ActiveLayerTracker.cpp @@ -20,6 +20,7 @@ #include "nsStyleTransformMatrix.h" #include "nsTransitionManager.h" #include "nsDisplayList.h" +#include "nsDOMCSSDeclaration.h" namespace mozilla { @@ -311,12 +312,21 @@ ActiveLayerTracker::NotifyOffsetRestyle(nsIFrame* aFrame) } /* static */ void -ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty) +ActiveLayerTracker::NotifyAnimated(nsIFrame* aFrame, + nsCSSProperty aProperty, + const nsAString& aNewValue, + nsDOMCSSDeclaration* aDOMCSSDecl) { LayerActivity* layerActivity = GetLayerActivityForUpdate(aFrame); uint8_t& mutationCount = layerActivity->RestyleCountForProperty(aProperty); - // We know this is animated, so just hack the mutation count. - mutationCount = 0xFF; + if (mutationCount != 0xFF) { + nsAutoString oldValue; + aDOMCSSDecl->GetPropertyValue(aProperty, oldValue); + if (aNewValue != oldValue) { + // We know this is animated, so just hack the mutation count. + mutationCount = 0xFF; + } + } } /* static */ void @@ -354,10 +364,12 @@ IsPresContextInScriptAnimationCallback(nsPresContext* aPresContext) /* static */ void ActiveLayerTracker::NotifyInlineStyleRuleModified(nsIFrame* aFrame, - nsCSSProperty aProperty) + nsCSSProperty aProperty, + const nsAString& aNewValue, + nsDOMCSSDeclaration* aDOMCSSDecl) { if (IsPresContextInScriptAnimationCallback(aFrame->PresContext())) { - NotifyAnimated(aFrame, aProperty); + NotifyAnimated(aFrame, aProperty, aNewValue, aDOMCSSDecl); } if (gLayerActivityTracker && gLayerActivityTracker->mCurrentScrollHandlerFrame.IsAlive()) { diff --git a/layout/base/ActiveLayerTracker.h b/layout/base/ActiveLayerTracker.h index 302b0ba13d..479fb89a1f 100644 --- a/layout/base/ActiveLayerTracker.h +++ b/layout/base/ActiveLayerTracker.h @@ -10,6 +10,7 @@ class nsIFrame; class nsIContent; class nsDisplayListBuilder; +class nsDOMCSSDeclaration; namespace mozilla { @@ -47,8 +48,12 @@ public: /** * Mark aFrame as being known to have an animation of aProperty. * Any such marking will time out after a short period. + * aNewValue and aDOMCSSDecl are used to determine whether the property's + * value has changed. */ - static void NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty); + static void NotifyAnimated(nsIFrame* aFrame, nsCSSProperty aProperty, + const nsAString& aNewValue, + nsDOMCSSDeclaration* aDOMCSSDecl); /** * Notify aFrame as being known to have an animation of aProperty through an * inline style modification during aScrollFrame's scroll event handler. @@ -60,8 +65,12 @@ public: * has been modified. * This notification is incomplete --- not all modifications to inline * style will trigger this. + * aNewValue and aDOMCSSDecl are used to determine whether the property's + * value has changed. */ - static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSProperty aProperty); + static void NotifyInlineStyleRuleModified(nsIFrame* aFrame, nsCSSProperty aProperty, + const nsAString& aNewValue, + nsDOMCSSDeclaration* aDOMCSSDecl); /** * Return true if aFrame's aProperty style should be considered as being animated * for pre-rendering. diff --git a/layout/base/GeometryUtils.cpp b/layout/base/GeometryUtils.cpp index 74776eeb56..fc320607cf 100644 --- a/layout/base/GeometryUtils.cpp +++ b/layout/base/GeometryUtils.cpp @@ -140,8 +140,8 @@ GetBoxRectForFrame(nsIFrame** aFrame, CSSBoxType aType) { nsRect r; nsIFrame* f = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(*aFrame, &r); - if (f) { - // For SVG, the BoxType is ignored. + if (f && f != *aFrame) { + // For non-outer SVG frames, the BoxType is ignored. *aFrame = f; return r; } diff --git a/layout/base/gtest/TestAccessibleCaretManager.cpp b/layout/base/gtest/TestAccessibleCaretManager.cpp index 3a90a5d5c4..78ec6eea99 100644 --- a/layout/base/gtest/TestAccessibleCaretManager.cpp +++ b/layout/base/gtest/TestAccessibleCaretManager.cpp @@ -60,8 +60,8 @@ public: using AccessibleCaretManager::UpdateCarets; using AccessibleCaretManager::HideCarets; using AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent; - using AccessibleCaretManager::sCaretsExtendedVisibility; using AccessibleCaretManager::sCaretsAlwaysTilt; + using AccessibleCaretManager::sCaretsAlwaysShowWhenScrolling; MockAccessibleCaretManager() : AccessibleCaretManager(nullptr) @@ -350,6 +350,11 @@ TEST_F(AccessibleCaretManagerTester, TestTypingAtEndOfInput) TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionMode) { + // Simulate B2G preference. + AutoRestore savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + EXPECT_CALL(mManager, GetCaretMode()) .WillRepeatedly(Return(CaretMode::Selection)); @@ -422,9 +427,13 @@ TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionMode) check.Call("scrollend2"); } -TEST_F(AccessibleCaretManagerTester, - TestScrollInSelectionModeWithExtendedVisibilityAndAlwaysTilt) +TEST_F(AccessibleCaretManagerTester, TestScrollInSelectionModeWithAlwaysTiltPref) { + // Simulate Firefox Android preference. + AutoRestore saveCaretsAlwaysTilt( + MockAccessibleCaretManager::sCaretsAlwaysTilt); + MockAccessibleCaretManager::sCaretsAlwaysTilt = true; + EXPECT_CALL(mManager, GetCaretMode()) .WillRepeatedly(Return(CaretMode::Selection)); @@ -441,7 +450,7 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("updatecarets")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)); + CaretChangedReason::Scroll)); EXPECT_CALL(check, Call("scrollstart1")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( @@ -458,7 +467,7 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("scrollend1")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)); + CaretChangedReason::Scroll)); EXPECT_CALL(check, Call("scrollstart2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( @@ -471,14 +480,6 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("scrollend2")); } - // Simulate Firefox Android preferences. - AutoRestore saveCaretsExtendedVisibility( - MockAccessibleCaretManager::sCaretsExtendedVisibility); - MockAccessibleCaretManager::sCaretsExtendedVisibility = true; - AutoRestore saveCaretsAlwaysTilt( - MockAccessibleCaretManager::sCaretsAlwaysTilt); - MockAccessibleCaretManager::sCaretsAlwaysTilt = true; - mManager.UpdateCarets(); EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); @@ -486,12 +487,12 @@ TEST_F(AccessibleCaretManagerTester, mManager.OnScrollStart(); EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); - EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); check.Call("scrollstart1"); mManager.OnReflow(); EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); - EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(SecondCaretAppearance(), Appearance::Right); check.Call("reflow1"); mManager.OnScrollEnd(); @@ -500,12 +501,12 @@ TEST_F(AccessibleCaretManagerTester, check.Call("scrollend1"); mManager.OnScrollStart(); - EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Left); EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); check.Call("scrollstart2"); mManager.OnReflow(); - EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown); + EXPECT_EQ(FirstCaretAppearance(), Appearance::Left); EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown); check.Call("reflow2"); @@ -517,6 +518,11 @@ TEST_F(AccessibleCaretManagerTester, TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible) { + // Simulate B2G preference. + AutoRestore savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + EXPECT_CALL(mManager, GetCaretMode()) .WillRepeatedly(Return(CaretMode::Cursor)); @@ -577,6 +583,11 @@ TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible) TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden) { + // Simulate B2G preference. + AutoRestore savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + EXPECT_CALL(mManager, GetCaretMode()) .WillRepeatedly(Return(CaretMode::Cursor)); @@ -631,6 +642,11 @@ TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenHidden) TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeOnEmptyContent) { + // Simulate B2G preference. + AutoRestore savesCaretsAlwaysShowWhenScrolling( + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling); + MockAccessibleCaretManager::sCaretsAlwaysShowWhenScrolling = false; + EXPECT_CALL(mManager, GetCaretMode()) .WillRepeatedly(Return(CaretMode::Cursor)); @@ -700,8 +716,13 @@ TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeOnEmptyContent) } TEST_F(AccessibleCaretManagerTester, - TestScrollInCursorModeOnEmptyContentWithSpecialPreference) + TestScrollInCursorModeWithCaretShownWhenLongTappingOnEmptyContentPref) { + // Simulate Firefox Android preference. + AutoRestore savesCaretShownWhenLongTappingOnEmptyContent( + MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent); + MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = true; + EXPECT_CALL(mManager, GetCaretMode()) .WillRepeatedly(Return(CaretMode::Cursor)); @@ -721,7 +742,7 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("longtap updatecarets")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)); + CaretChangedReason::Scroll)); EXPECT_CALL(check, Call("longtap scrollstart1")); EXPECT_CALL(mManager.FirstCaret(), SetPosition(_, _)) @@ -731,7 +752,7 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("longtap scrollend1")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)); + CaretChangedReason::Scroll)); EXPECT_CALL(check, Call("longtap scrollstart2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( @@ -739,7 +760,7 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("longtap scrollend2")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( - CaretChangedReason::Visibilitychange)); + CaretChangedReason::Scroll)); EXPECT_CALL(check, Call("longtap scrollstart3")); EXPECT_CALL(mManager, DispatchCaretStateChangedEvent( @@ -747,10 +768,6 @@ TEST_F(AccessibleCaretManagerTester, EXPECT_CALL(check, Call("longtap scrollend3")); } - AutoRestore savePref( - MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent); - MockAccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = true; - // Simulate a single tap on an empty input. mManager.FirstCaret().SetAppearance(Appearance::None); mManager.UpdateCarets(); diff --git a/layout/base/moz.build b/layout/base/moz.build index a1b42b7d98..d778e061a1 100644 --- a/layout/base/moz.build +++ b/layout/base/moz.build @@ -190,6 +190,11 @@ LOCAL_INCLUDES += [ '/view', ] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': + LOCAL_INCLUDES += [ + '/widget/android', + ] + FINAL_LIBRARY = 'xul' MOCHITEST_MANIFESTS += ['tests/mochitest.ini'] diff --git a/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py b/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py index f404dcf7f0..2441a53596 100644 --- a/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py +++ b/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py @@ -208,6 +208,42 @@ class AccessibleCaretSelectionModeTestCase(MarionetteTestCase): self.assertEqual(target_content, sel.selected_content) + @parameterized(_input_id, el_id=_input_id) + @parameterized(_textarea_id, el_id=_textarea_id) + @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) + @parameterized(_contenteditable_id, el_id=_contenteditable_id) + @parameterized(_content_id, el_id=_content_id) + def test_drag_swappable_carets(self, el_id): + self.open_test_html(self._selection_html) + el = self.marionette.find_element(By.ID, el_id) + sel = SelectionManager(el) + original_content = sel.content + words = original_content.split() + self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.') + + target_content1 = words[0] + target_content2 = original_content[len(words[0]):] + + # Get the location of the carets at the end of the content for later + # use. + el.tap() + sel.select_all() + end_caret_x, end_caret_y = sel.second_caret_location() + + self.long_press_on_word(el, 0) + + # Drag the first caret to the end and back to where it was + # immediately. The selection range should not be collapsed. + caret1_x, caret1_y = sel.first_caret_location() + self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y)\ + .flick(el, end_caret_x, end_caret_y, caret1_x, caret1_y).perform() + self.assertEqual(target_content1, sel.selected_content) + + # Drag the first caret to the end. + caret1_x, caret1_y = sel.first_caret_location() + self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y).perform() + self.assertEqual(target_content2, sel.selected_content) + @parameterized(_input_id, el_id=_input_id) @parameterized(_textarea_id, el_id=_textarea_id) @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id) @@ -414,6 +450,29 @@ class AccessibleCaretSelectionModeTestCase(MarionetteTestCase): self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), '4\nuser can select this 5\nuser') + def test_drag_swappable_caret_over_non_selectable_field(self): + self.open_test_html(self._multiplerange_html) + body = self.marionette.find_element(By.ID, 'bd') + sel3 = self.marionette.find_element(By.ID, 'sel3') + sel4 = self.marionette.find_element(By.ID, 'sel4') + sel = SelectionManager(body) + + self.long_press_on_word(sel4, 3) + (end_caret1_x, end_caret1_y), (end_caret2_x, end_caret2_y) = sel.carets_location() + + self.long_press_on_word(sel3, 3) + (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location() + + # Drag the first caret down, which will across the second caret. + self.actions.flick(body, caret1_x, caret1_y, end_caret1_x, end_caret1_y).perform() + self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), + '3\nuser can select') + + # The old second caret becomes the first caret. Drag it down again. + self.actions.flick(body, caret2_x, caret2_y, end_caret2_x, end_caret2_y).perform() + self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), + 'this') + def test_drag_caret_to_beginning_of_a_line(self): '''Bug 1094056 Test caret visibility when caret is dragged to beginning of a line diff --git a/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html b/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html index 295c0b392f..7ec4f7b56d 100644 --- a/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html +++ b/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html @@ -336,7 +336,7 @@ TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText
- + @@ -660,19 +660,26 @@ function runTest() { var svgContainerX = svgContainer.getBoundingClientRect().left; var svgContainerY = svgContainer.getBoundingClientRect().top; checkQuadIsRect("circle", {}, - svgContainerX + 30, svgContainerY + 30, 40, 40); + svgContainerX + 41, svgContainerY + 41, 40, 40); // Box types are ignored for SVG elements. checkQuadIsRect("circle", {box:"content"}, - svgContainerX + 30, svgContainerY + 30, 40, 40); + svgContainerX + 41, svgContainerY + 41, 40, 40); checkQuadIsRect("circle", {box:"padding"}, - svgContainerX + 30, svgContainerY + 30, 40, 40); + svgContainerX + 41, svgContainerY + 41, 40, 40); checkQuadIsRect("circle", {box:"margin"}, - svgContainerX + 30, svgContainerY + 30, 40, 40); + svgContainerX + 41, svgContainerY + 41, 40, 40); checkQuadIsRect("d", {toStr:"circle"}, - dX - (svgContainerX + 30), dY - (svgContainerY + 30), dW, dH); + dX - (svgContainerX + 41), dY - (svgContainerY + 41), dW, dH); // Test foreignObject inside an SVG transform. checkQuadIsRect("foreign", {}, - svgContainerX + 100, svgContainerY + 40, 200, 120); + svgContainerX + 111, svgContainerY + 51, 200, 120); + // Outer elements support padding and content boxes + checkQuadIsRect("svg", {box:"border"}, + svgContainerX, svgContainerY, 222, 222); + checkQuadIsRect("svg", {box:"padding"}, + svgContainerX + 7, svgContainerY + 7, 208, 208); + checkQuadIsRect("svg", {box:"content"}, + svgContainerX + 11, svgContainerY + 11, 200, 200); // XXX Test SVG text (probably broken; unclear what the best way is to handle it) diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 02b91db928..6eaa57095d 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -263,7 +263,7 @@ static void Shutdown(); #include "GMPService.h" #include "mozilla/dom/PresentationDeviceManager.h" -#include "mozilla/dom/PresentationSessionTransport.h" +#include "mozilla/dom/PresentationTCPSessionTransport.h" #include "mozilla/TextInputProcessor.h" @@ -298,7 +298,7 @@ using mozilla::dom::NotificationTelemetryService; #define PRESENTATION_DEVICE_MANAGER_CID \ { 0xe1e79dec, 0x4085, 0x4994, { 0xac, 0x5b, 0x74, 0x4b, 0x01, 0x66, 0x97, 0xe6 } } -#define PRESENTATION_SESSION_TRANSPORT_CID \ +#define PRESENTATION_TCP_SESSION_TRANSPORT_CID \ { 0xc9d023f4, 0x6228, 0x4c07, { 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } } already_AddRefed NS_CreatePresentationService(); @@ -405,7 +405,7 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(FakeInputPortService, NS_GENERIC_FACTORY_CONSTRUCTOR(InputPortData) NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPresentationService, NS_CreatePresentationService) -NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationSessionTransport) +NS_GENERIC_FACTORY_CONSTRUCTOR(PresentationTCPSessionTransport) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NotificationTelemetryService, Init) #ifndef MOZ_SIMPLEPUSH @@ -872,7 +872,7 @@ NS_DEFINE_NAMED_CID(GECKO_MEDIA_PLUGIN_SERVICE_CID); NS_DEFINE_NAMED_CID(PRESENTATION_SERVICE_CID); NS_DEFINE_NAMED_CID(PRESENTATION_DEVICE_MANAGER_CID); -NS_DEFINE_NAMED_CID(PRESENTATION_SESSION_TRANSPORT_CID); +NS_DEFINE_NAMED_CID(PRESENTATION_TCP_SESSION_TRANSPORT_CID); NS_DEFINE_NAMED_CID(TEXT_INPUT_PROCESSOR_CID); @@ -1171,7 +1171,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kTV_PROGRAM_DATA_CID, false, nullptr, TVProgramDataConstructor }, { &kPRESENTATION_SERVICE_CID, false, nullptr, nsIPresentationServiceConstructor }, { &kPRESENTATION_DEVICE_MANAGER_CID, false, nullptr, PresentationDeviceManagerConstructor }, - { &kPRESENTATION_SESSION_TRANSPORT_CID, false, nullptr, PresentationSessionTransportConstructor }, + { &kPRESENTATION_TCP_SESSION_TRANSPORT_CID, false, nullptr, PresentationTCPSessionTransportConstructor }, { &kTEXT_INPUT_PROCESSOR_CID, false, nullptr, TextInputProcessorConstructor }, { &kFAKE_INPUTPORT_SERVICE_CID, false, nullptr, FakeInputPortServiceConstructor }, { &kINPUTPORT_DATA_CID, false, nullptr, InputPortDataConstructor }, @@ -1340,7 +1340,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { NS_VOICEMAIL_SERVICE_CONTRACTID, &kNS_VOICEMAIL_SERVICE_CID }, { PRESENTATION_SERVICE_CONTRACTID, &kPRESENTATION_SERVICE_CID }, { PRESENTATION_DEVICE_MANAGER_CONTRACTID, &kPRESENTATION_DEVICE_MANAGER_CID }, - { PRESENTATION_SESSION_TRANSPORT_CONTRACTID, &kPRESENTATION_SESSION_TRANSPORT_CID }, + { PRESENTATION_TCP_SESSION_TRANSPORT_CONTRACTID, &kPRESENTATION_TCP_SESSION_TRANSPORT_CID }, { "@mozilla.org/text-input-processor;1", &kTEXT_INPUT_PROCESSOR_CID }, { FAKE_INPUTPORT_SERVICE_CONTRACTID, &kFAKE_INPUTPORT_SERVICE_CID }, { INPUTPORT_DATA_CONTRACTID, &kINPUTPORT_DATA_CID }, diff --git a/layout/printing/nsPrintEngine.cpp b/layout/printing/nsPrintEngine.cpp index 2305f53743..0e5e3437d5 100644 --- a/layout/printing/nsPrintEngine.cpp +++ b/layout/printing/nsPrintEngine.cpp @@ -1508,6 +1508,10 @@ void nsPrintEngine::FirePrintingErrorEvent(nsresult aPrintError) { nsCOMPtr cv = do_QueryInterface(mDocViewerPrint); + if (NS_WARN_IF(!cv)) { + return; + } + nsCOMPtr doc = cv->GetDocument(); RefPtr event = NS_NewDOMCustomEvent(doc, nullptr, nullptr); diff --git a/layout/reftests/css-parsing/reftest.list b/layout/reftests/css-parsing/reftest.list index bc869a1cfe..0044b6150e 100644 --- a/layout/reftests/css-parsing/reftest.list +++ b/layout/reftests/css-parsing/reftest.list @@ -6,3 +6,4 @@ == at-rule-error-handling-media-1.html at-rule-error-handling-ref.html == invalid-font-face-descriptor-1.html invalid-font-face-descriptor-1-ref.html == two-dash-identifiers.html two-dash-identifiers-ref.html +== supports-moz-bool-pref.html supports-moz-bool-pref-ref.html diff --git a/layout/reftests/css-parsing/supports-moz-bool-pref-ref.html b/layout/reftests/css-parsing/supports-moz-bool-pref-ref.html new file mode 100644 index 0000000000..4f067bbbfc --- /dev/null +++ b/layout/reftests/css-parsing/supports-moz-bool-pref-ref.html @@ -0,0 +1,10 @@ + + + + + +

This text should not have background color.

+ + + diff --git a/layout/reftests/css-parsing/supports-moz-bool-pref.html b/layout/reftests/css-parsing/supports-moz-bool-pref.html new file mode 100644 index 0000000000..fe58ab766d --- /dev/null +++ b/layout/reftests/css-parsing/supports-moz-bool-pref.html @@ -0,0 +1,20 @@ + + + + + + +

This text should not have background color.

+ + + diff --git a/layout/reftests/svg/mask-and-clipPath-ref.html b/layout/reftests/svg/mask-and-clipPath-ref.html new file mode 100644 index 0000000000..86098fa835 --- /dev/null +++ b/layout/reftests/svg/mask-and-clipPath-ref.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/mask-and-clipPath.html b/layout/reftests/svg/mask-and-clipPath.html new file mode 100644 index 0000000000..19759a6a31 --- /dev/null +++ b/layout/reftests/svg/mask-and-clipPath.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/xul/reftest.list b/layout/reftests/xul/reftest.list index 17f43731d9..15d6982048 100644 --- a/layout/reftests/xul/reftest.list +++ b/layout/reftests/xul/reftest.list @@ -5,10 +5,11 @@ random-if(Android||B2G) fails-if(winWidget) skip-if((B2G&&browserIsRemote)||Mule skip-if((B2G&&browserIsRemote)||Mulet) == textbox-overflow-1.xul textbox-overflow-1-ref.xul # for bug 749658 # Initial mulet triage: parity with B2G/B2G Desktop # accesskeys are not normally displayed on Mac, so skip this test skip-if(cocoaWidget) skip-if((B2G&&browserIsRemote)||Mulet) == accesskey.xul accesskey-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop -fails-if(cocoaWidget) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,11) skip-if((B2G&&browserIsRemote)||Mulet) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop +fails-if(cocoaWidget) fails-if(browserIsRemote&&d2d) fuzzy-if(xulRuntime.widgetToolkit=="gtk3",1,11) skip-if((B2G&&browserIsRemote)||Mulet) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop, win8: bug 1254832 skip-if((B2G&&browserIsRemote)||Mulet) != tree-row-outline-1.xul tree-row-outline-1-notref.xul # Initial mulet triage: parity with B2G/B2G Desktop skip-if((B2G&&browserIsRemote)||Mulet) == text-small-caps-1.xul text-small-caps-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop -skip-if((B2G&&browserIsRemote)||Mulet) fuzzy-if(skiaContent,1,60) == inactive-fixed-bg-bug1205630.xul inactive-fixed-bg-bug1205630-ref.html +skip-if((B2G&&browserIsRemote)||Mulet) fuzzy-if(skiaContent,1,60) fuzzy-if(cocoaWidget&&browserIsRemote&&!skiaContent,1,31) fuzzy-if(winWidget&&browserIsRemote&&layersGPUAccelerated,1,50) == inactive-fixed-bg-bug1205630.xul inactive-fixed-bg-bug1205630-ref.html + # Tests for XUL with 'object-fit' & 'object-position': # These tests should be very similar to tests in our w3c-css/submitted/images3 @@ -43,8 +44,8 @@ skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-fill-svg-001.xul object-fit skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-fill-svg-002.xul object-fit-fill-svg-002-ref.html skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-fill-svg-003.xul object-fit-fill-svg-003-ref.html skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-fill-svg-004.xul object-fit-fill-svg-004-ref.html -skip-if((B2G&&browserIsRemote)||Mulet) fails == object-fit-fill-svg-005.xul object-fit-fill-svg-005-ref.html # bug 1092436 -skip-if((B2G&&browserIsRemote)||Mulet) fails == object-fit-fill-svg-006.xul object-fit-fill-svg-006-ref.html # bug 1092436 +fails skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-fill-svg-005.xul object-fit-fill-svg-005-ref.html # bug 1092436 +fails skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-fill-svg-006.xul object-fit-fill-svg-006-ref.html # bug 1092436 skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-none-png-001.xul object-fit-none-png-001-ref.html skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-none-png-002.xul object-fit-none-png-002-ref.html skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-none-svg-001.xul object-fit-none-svg-001-ref.html @@ -64,4 +65,10 @@ skip-if((B2G&&browserIsRemote)||Mulet) == object-fit-scale-down-svg-006.xul obje skip-if((B2G&&browserIsRemote)||Mulet) == object-position-png-001.xul object-position-png-001-ref.html skip-if((B2G&&browserIsRemote)||Mulet) == object-position-png-002.xul object-position-png-002-ref.html -skip-if((B2G&&browserIsRemote)||Mulet) == inactive-fixed-bg-bug1205630.xul inactive-fixed-bg-bug1205630-ref.html +# Tests for rendering SVG images in a XUL : +# XXXdholbert: These are marked as "random" right now, since they might not +# render the images they trying to test in time for the reftest snapshot, per +# bug 1218954. Once that bug is fixed, we should replace the "random" +# annotation with "skip-if((B2G&&browserIsRemote)||Mulet)", like above tests. +skip == treecell-image-svg-1a.xul treecell-image-svg-1-ref.xul # bug 1218954 +skip == treecell-image-svg-1b.xul treecell-image-svg-1-ref.xul # bug 1218954 diff --git a/layout/reftests/xul/treecell-image-svg-1-ref.xul b/layout/reftests/xul/treecell-image-svg-1-ref.xul new file mode 100644 index 0000000000..029b140f01 --- /dev/null +++ b/layout/reftests/xul/treecell-image-svg-1-ref.xul @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/xul/treecell-image-svg-1a.xul b/layout/reftests/xul/treecell-image-svg-1a.xul new file mode 100644 index 0000000000..d481190345 --- /dev/null +++ b/layout/reftests/xul/treecell-image-svg-1a.xul @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/xul/treecell-image-svg-1b.xul b/layout/reftests/xul/treecell-image-svg-1b.xul new file mode 100644 index 0000000000..f13c138fda --- /dev/null +++ b/layout/reftests/xul/treecell-image-svg-1b.xul @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/layout/style/crashtests/1245260-1.html b/layout/style/crashtests/1245260-1.html new file mode 100644 index 0000000000..6f2dda9955 --- /dev/null +++ b/layout/style/crashtests/1245260-1.html @@ -0,0 +1,53 @@ + + + + +Bug 1245260 + + + + + + + diff --git a/layout/tables/crashtests/crashtests.list b/layout/tables/crashtests/crashtests.list index efa5f574a7..8c9e38ade4 100644 --- a/layout/tables/crashtests/crashtests.list +++ b/layout/tables/crashtests/crashtests.list @@ -151,3 +151,4 @@ load 1027611-1.html load 1031934.html load 1183896.html load 1223232.html +load 1243623-1.html diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 3ac5b95ae9..9e023a03b3 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -289,11 +289,14 @@ nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame) nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame, nsIFrame* aDestructRoot) { - // Retrieve the table frame, and ensure that we hit aDestructRoot on the way. - // If we don't, that means that the table frame will be destroyed, so we don't - // need to bother with unregistering this frame. - nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame); - if (!tableFrame) { + // Retrieve the table frame, and check if we hit aDestructRoot on the way. + bool didPassThrough; + nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame, + &didPassThrough); + if (!didPassThrough && !tableFrame->GetPrevContinuation()) { + // The table frame will be destroyed, and it's the first im flow (and thus + // owning the PositionedTablePartArray), so we don't need to do + // anything. return; } tableFrame = static_cast(tableFrame->FirstContinuation()); @@ -3829,19 +3832,20 @@ nsTableFrame::GetTableFrame(nsIFrame* aFrame) nsTableFrame* nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough, - nsIFrame* aFrame) + nsIFrame* aFrame, + bool* aDidPassThrough) { MOZ_ASSERT(aMustPassThrough == aFrame || nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame), "aMustPassThrough should be an ancestor"); - // Retrieve the table frame, and ensure that we hit aMustPassThrough on the - // way. If we don't, just return null. - bool hitPassThroughFrame = false; + // Retrieve the table frame, and check if we hit aMustPassThrough on the + // way. + *aDidPassThrough = false; nsTableFrame* tableFrame = nullptr; for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) { if (ancestor == aMustPassThrough) { - hitPassThroughFrame = true; + *aDidPassThrough = true; } if (nsGkAtoms::tableFrame == ancestor->GetType()) { tableFrame = static_cast(ancestor); @@ -3850,7 +3854,7 @@ nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough, } MOZ_ASSERT(tableFrame, "Should have a table frame here"); - return hitPassThroughFrame ? tableFrame : nullptr; + return tableFrame; } bool diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index 711b316bdf..683975ce33 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -218,11 +218,12 @@ public: /** helper method to find the table parent of any table frame object */ static nsTableFrame* GetTableFrame(nsIFrame* aSourceFrame); - /* Like GetTableFrame, but will return nullptr if we don't pass through - * aMustPassThrough on the way to the table. + /* Like GetTableFrame, but will set *aDidPassThrough to false if we don't + * pass through aMustPassThrough on the way to the table. */ static nsTableFrame* GetTableFramePassingThrough(nsIFrame* aMustPassThrough, - nsIFrame* aSourceFrame); + nsIFrame* aSourceFrame, + bool* aDidPassThrough); typedef void (* DisplayGenericTablePartTraversal) (nsDisplayListBuilder* aBuilder, nsFrame* aFrame, diff --git a/layout/tables/nsTablePainter.cpp b/layout/tables/nsTablePainter.cpp index 50a0e07ecb..cf0565a91b 100644 --- a/layout/tables/nsTablePainter.cpp +++ b/layout/tables/nsTablePainter.cpp @@ -230,7 +230,7 @@ TableBackgroundPainter::PaintTableFrame(nsTableFrame* aTableFrame, DrawResult result = DrawResult::SUCCESS; if (tableData.IsVisible()) { - result = + result &= nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext, tableData.mFrame, mDirtyRect, tableData.mRect + mRenderPt, @@ -276,15 +276,16 @@ TableBackgroundPainter::PaintTable(nsTableFrame* aTableFrame, if (rowGroups.Length() < 1) { //degenerate case if (aPaintTableBackground) { - PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0)); + result &= PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0)); } /* No cells; nothing else to paint */ return result; } if (aPaintTableBackground) { - PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1], - aDeflate); + result &= + PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1], + aDeflate); } /*Set up column background/border data*/ diff --git a/layout/tools/recording/recording.js b/layout/tools/recording/recording.js index 5d329e6060..552423c9fd 100644 --- a/layout/tools/recording/recording.js +++ b/layout/tools/recording/recording.js @@ -4,7 +4,7 @@ * 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/. */ -const CC = Components.classes; +var CC = Components.classes; const CI = Components.interfaces; const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1"; diff --git a/layout/tools/reftest/reftest-preferences.js b/layout/tools/reftest/reftest-preferences.js index a3278b0279..087cd1a85c 100644 --- a/layout/tools/reftest/reftest-preferences.js +++ b/layout/tools/reftest/reftest-preferences.js @@ -113,3 +113,6 @@ user_pref("startup.homepage_override_url", ""); user_pref("browser.usedOnWindows10.introURL", ""); user_pref("media.gmp-manager.url.override", "http://localhost/dummy-gmp-manager.xml"); + +// A fake bool pref for "@supports -moz-bool-pref" sanify test. +user_pref("testing.supports.moz-bool-pref", true); diff --git a/layout/xul/nsMenuBarListener.cpp b/layout/xul/nsMenuBarListener.cpp index f600389b3c..0879b2cd5e 100644 --- a/layout/xul/nsMenuBarListener.cpp +++ b/layout/xul/nsMenuBarListener.cpp @@ -408,6 +408,11 @@ nsMenuBarListener::MouseDown(nsIDOMEvent* aMouseEvent) nsresult nsMenuBarListener::HandleEvent(nsIDOMEvent* aEvent) { + // If the menu bar is collapsed, don't do anything. + if (!mMenuBarFrame->StyleVisibility()->IsVisible()) { + return NS_OK; + } + nsAutoString eventType; aEvent->GetType(eventType); diff --git a/layout/xul/nsMenuFrame.cpp b/layout/xul/nsMenuFrame.cpp index 3abe06f6b2..0624a35e95 100644 --- a/layout/xul/nsMenuFrame.cpp +++ b/layout/xul/nsMenuFrame.cpp @@ -418,6 +418,7 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, } else { if (!IsOpen()) { + menuParent->ChangeMenuItem(this, false, false); OpenMenu(false); } } @@ -466,7 +467,11 @@ nsMenuFrame::HandleEvent(nsPresContext* aPresContext, if (IsMenu() && !onmenubar && IsOpen()) { // Submenus don't get closed up immediately. } - else if (this == menuParent->GetCurrentMenuItem()) { + else if (this == menuParent->GetCurrentMenuItem() +#ifdef XP_WIN + && GetParentMenuListType() == eNotMenuList +#endif + ) { menuParent->ChangeMenuItem(nullptr, false, false); } } diff --git a/layout/xul/test/chrome.ini b/layout/xul/test/chrome.ini index a3b151e016..b227ccd584 100644 --- a/layout/xul/test/chrome.ini +++ b/layout/xul/test/chrome.ini @@ -22,5 +22,6 @@ skip-if = os == 'linux' # No native mousedown event on Linux [test_popupZoom.xul] [test_resizer.xul] [test_stack.xul] +[test_submenuClose.xul] [test_windowminmaxsize.xul] skip-if = buildapp == 'mulet' diff --git a/layout/xul/test/mochitest.ini b/layout/xul/test/mochitest.ini index 41d1df97f2..dc166ae1ed 100644 --- a/layout/xul/test/mochitest.ini +++ b/layout/xul/test/mochitest.ini @@ -9,3 +9,5 @@ skip-if = toolkit == 'android' #bug 798806 [test_resizer_incontent.xul] [test_splitter.xul] skip-if = toolkit == 'android' # no XUL theme +[test_bug1197913.xul] +skip-if = toolkit == 'android' diff --git a/layout/xul/test/test_bug1197913.xul b/layout/xul/test/test_bug1197913.xul new file mode 100644 index 0000000000..5665e0c2ca --- /dev/null +++ b/layout/xul/test/test_bug1197913.xul @@ -0,0 +1,67 @@ + + + + + + + diff --git a/layout/xul/test/test_submenuClose.xul b/layout/xul/test/test_submenuClose.xul new file mode 100644 index 0000000000..907736d999 --- /dev/null +++ b/layout/xul/test/test_submenuClose.xul @@ -0,0 +1,91 @@ + + + + + + + diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 823a97dfbf..fa34c6614f 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -1192,7 +1192,7 @@ nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const n nsRect twistyRect(cellRect); nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext, - rc, twistyContext); + twistyContext); if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) { // If we're looking for the twisty Rect, just return the size @@ -1557,7 +1557,7 @@ nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect, nsRect imageSize; GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext, - rc, twistyContext); + twistyContext); // We will treat a click as hitting the twisty if it happens on the margins, borders, padding, // or content of the twisty object. By allowing a "slop" into the margin, we make it a little @@ -1716,7 +1716,7 @@ nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol, nsRect imageSize; nsRect twistyRect(cellRect); GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(), - *aRenderingContext, twistyContext); + twistyContext); // Add in the margins of the twisty element. nsMargin twistyMargin; @@ -2060,7 +2060,6 @@ nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex, nsRect& aImageRect, nsRect& aTwistyRect, nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, nsStyleContext* aTwistyContext) { // The twisty rect extends all the way to the end of the cell. This is incorrect. We need to @@ -3262,7 +3261,7 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex, nsRect imageSize; nsRect twistyRect(aCellRect); GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext, - aRenderingContext, twistyContext); + twistyContext); nsMargin twistyMargin; twistyContext->StyleMargin()->GetMargin(twistyMargin); @@ -3368,7 +3367,8 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex, switch (aColumn->GetType()) { case nsITreeColumn::TYPE_TEXT: case nsITreeColumn::TYPE_PASSWORD: - PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX); + result &= PaintText(aRowIndex, aColumn, elementRect, aPresContext, + aRenderingContext, aDirtyRect, currX); break; case nsITreeColumn::TYPE_CHECKBOX: result &= PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, @@ -3386,7 +3386,8 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex, break; case nsITreeView::PROGRESS_NONE: default: - PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX); + result &= PaintText(aRowIndex, aColumn, elementRect, aPresContext, + aRenderingContext, aDirtyRect, currX); break; } break; @@ -3436,7 +3437,7 @@ nsTreeBodyFrame::PaintTwisty(int32_t aRowIndex, nsRect imageSize; nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, - aPresContext, aRenderingContext, twistyContext); + aPresContext, twistyContext); // Subtract out the remaining width. This is done even when we don't actually paint a twisty in // this cell, so that cells in different rows still line up. @@ -3611,26 +3612,44 @@ nsTreeBodyFrame::PaintImage(int32_t aRowIndex, // Deflate destRect for the border and padding. destRect.Deflate(bp); - // Get the image source rectangle - the rectangle containing the part of - // the image that we are going to display. - // sourceRect will be passed as the aSrcRect argument in the DrawImage method. - nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image); - - // Let's say that the image is 100 pixels tall and - // that the CSS has specified that the destination height should be 50 - // pixels tall. Let's say that the cell height is only 20 pixels. So, in - // those 20 visible pixels, we want to see the top 20/50ths of the image. - // So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels. - // Essentially, we are scaling the image as dictated by the CSS destination - // height and width, and we are then clipping the scaled image by the cell - // width and height. + // Compute the area where our whole image would be mapped, to get the + // desired subregion onto our actual destRect: + nsRect wholeImageDest; CSSIntSize rawImageCSSIntSize; - image->GetWidth(&rawImageCSSIntSize.width); - image->GetHeight(&rawImageCSSIntSize.height); - nsSize rawImageSize(CSSPixel::ToAppUnits(rawImageCSSIntSize)); - nsRect wholeImageDest = - nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect, - nsRect(destRect.TopLeft(), imageDestSize)); + if (NS_SUCCEEDED(image->GetWidth(&rawImageCSSIntSize.width)) && + NS_SUCCEEDED(image->GetHeight(&rawImageCSSIntSize.height))) { + // Get the image source rectangle - the rectangle containing the part of + // the image that we are going to display. sourceRect will be passed as + // the aSrcRect argument in the DrawImage method. + nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image); + + // Let's say that the image is 100 pixels tall and that the CSS has + // specified that the destination height should be 50 pixels tall. Let's + // say that the cell height is only 20 pixels. So, in those 20 visible + // pixels, we want to see the top 20/50ths of the image. So, the + // sourceRect.height should be 100 * 20 / 50, which is 40 pixels. + // Essentially, we are scaling the image as dictated by the CSS + // destination height and width, and we are then clipping the scaled + // image by the cell width and height. + nsSize rawImageSize(CSSPixel::ToAppUnits(rawImageCSSIntSize)); + wholeImageDest = + nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect, + nsRect(destRect.TopLeft(), + imageDestSize)); + } else { + // GetWidth/GetHeight failed, so we can't easily map a subregion of the + // source image onto the destination area. + // * If this happens with a RasterImage, it probably means the image is + // in an error state, and we shouldn't draw anything. Hence, we leave + // wholeImageDest as an empty rect (its initial state). + // * If this happens with a VectorImage, it probably means the image has + // no explicit width or height attribute -- but we can still proceed and + // just treat the destination area as our whole SVG image area. Hence, we + // set wholeImageDest to the full destRect. + if (image->GetType() == imgIContainer::TYPE_VECTOR) { + wholeImageDest = destRect; + } + } gfxContext* ctx = aRenderingContext.ThebesContext(); if (opacity != 1.0f) { @@ -3658,7 +3677,7 @@ nsTreeBodyFrame::PaintImage(int32_t aRowIndex, return result; } -void +DrawResult nsTreeBodyFrame::PaintText(int32_t aRowIndex, nsTreeColumn* aColumn, const nsRect& aTextRect, @@ -3683,8 +3702,12 @@ nsTreeBodyFrame::PaintText(int32_t aRowIndex, // necessary CheckTextForBidi(text); - if (text.Length() == 0) - return; // Don't paint an empty string. XXX What about background/borders? Still paint? + DrawResult result = DrawResult::SUCCESS; + + if (text.Length() == 0) { + // Don't paint an empty string. XXX What about background/borders? Still paint? + return result; + } int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel(); DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); @@ -3729,7 +3752,8 @@ nsTreeBodyFrame::PaintText(int32_t aRowIndex, if (!isRTL) aCurrX += textRect.width + textMargin.LeftRight(); - PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect); + result &= PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, + textRect, aDirtyRect); // Time to paint our text. textRect.Deflate(bp); @@ -3783,6 +3807,7 @@ nsTreeBodyFrame::PaintText(int32_t aRowIndex, ctx->PopGroupAndBlend(); } + return result; } DrawResult @@ -4016,8 +4041,8 @@ nsTreeBodyFrame::PaintDropFeedback(const nsRect& aDropFeedbackRect, nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty); nsRect imageSize; nsRect twistyRect; - GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, aPresContext, - aRenderingContext, twistyContext); + GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, + aPresContext, twistyContext); nsMargin twistyMargin; twistyContext->StyleMargin()->GetMargin(twistyMargin); twistyRect.Inflate(twistyMargin); diff --git a/layout/xul/tree/nsTreeBodyFrame.h b/layout/xul/tree/nsTreeBodyFrame.h index 0e344731a1..bf7a2fcba6 100644 --- a/layout/xul/tree/nsTreeBodyFrame.h +++ b/layout/xul/tree/nsTreeBodyFrame.h @@ -262,13 +262,13 @@ protected: nscoord& aCurrX); // This method paints the text string inside a particular cell of the tree. - void PaintText(int32_t aRowIndex, - nsTreeColumn* aColumn, - const nsRect& aTextRect, - nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, - const nsRect& aDirtyRect, - nscoord& aCurrX); + DrawResult PaintText(int32_t aRowIndex, + nsTreeColumn* aColumn, + const nsRect& aTextRect, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nscoord& aCurrX); // This method paints the checkbox inside a particular cell of the tree. DrawResult PaintCheckbox(int32_t aRowIndex, @@ -332,7 +332,6 @@ protected: nsRect& aImageRect, nsRect& aTwistyRect, nsPresContext* aPresContext, - nsRenderingContext& aRenderingContext, nsStyleContext* aTwistyContext); // Fetch an image from the image cache. diff --git a/layout/xul/tree/nsTreeColumns.cpp b/layout/xul/tree/nsTreeColumns.cpp index 0d344ee964..45e5a8adaf 100644 --- a/layout/xul/tree/nsTreeColumns.cpp +++ b/layout/xul/tree/nsTreeColumns.cpp @@ -395,8 +395,7 @@ nsTreeColumn::Invalidate(mozilla::ErrorResult& aRv) } nsTreeColumns::nsTreeColumns(nsTreeBodyFrame* aTree) - : mTree(aTree), - mFirstColumn(nullptr) + : mTree(aTree) { } @@ -671,7 +670,7 @@ nsTreeColumns::InvalidateColumns() currCol = currCol->GetNext()) { currCol->SetColumns(nullptr); } - NS_IF_RELEASE(mFirstColumn); + mFirstColumn = nullptr; return NS_OK; } @@ -761,7 +760,7 @@ nsTreeColumns::EnsureColumns() col->SetPrevious(currCol); } else { - NS_ADDREF(mFirstColumn = col); + mFirstColumn = col; } currCol = col; } diff --git a/layout/xul/tree/nsTreeColumns.h b/layout/xul/tree/nsTreeColumns.h index 00f77f7be5..54fed19663 100644 --- a/layout/xul/tree/nsTreeColumns.h +++ b/layout/xul/tree/nsTreeColumns.h @@ -211,7 +211,7 @@ private: * XXX this means that new nsTreeColumn objects are unnecessarily created * for untouched columns. */ - nsTreeColumn* mFirstColumn; + RefPtr mFirstColumn; }; #endif // nsTreeColumns_h__ diff --git a/layout/xul/tree/nsTreeContentView.cpp b/layout/xul/tree/nsTreeContentView.cpp index 3dfbb2c201..66cc0072ff 100644 --- a/layout/xul/tree/nsTreeContentView.cpp +++ b/layout/xul/tree/nsTreeContentView.cpp @@ -171,7 +171,7 @@ nsTreeContentView::GetRowProperties(int32_t aIndex, nsAString& aProps) if (aIndex < 0 || aIndex >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); nsIContent* realRow; if (row->IsSeparator()) realRow = row->mContent; @@ -194,7 +194,7 @@ nsTreeContentView::GetCellProperties(int32_t aRow, nsITreeColumn* aCol, if (aRow < 0 || aRow >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); if (realRow) { @@ -323,10 +323,10 @@ nsTreeContentView::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* if (parentIndex >= 0) { // Compute the last index in this subtree. int32_t lastIndex = parentIndex + (mRows[parentIndex])->mSubtreeSize; - Row* row = mRows[lastIndex]; + Row* row = mRows[lastIndex].get(); while (row->mParentIndex != parentIndex) { lastIndex = row->mParentIndex; - row = mRows[lastIndex]; + row = mRows[lastIndex].get(); } *_retval = aRowIndex < lastIndex; @@ -346,10 +346,10 @@ nsTreeContentView::GetLevel(int32_t aIndex, int32_t* _retval) return NS_ERROR_INVALID_ARG; int32_t level = 0; - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); while (row->mParentIndex >= 0) { level++; - row = mRows[row->mParentIndex]; + row = mRows[row->mParentIndex].get(); } *_retval = level; @@ -365,7 +365,7 @@ nsTreeContentView::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& _re if (aRow < 0 || aRow >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -388,7 +388,7 @@ nsTreeContentView::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* _ *_retval = nsITreeView::PROGRESS_NONE; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -417,7 +417,7 @@ nsTreeContentView::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& _r if (aRow < 0 || aRow >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -441,7 +441,7 @@ nsTreeContentView::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& _re if (aRow < 0 || aRow >= int32_t(mRows.Length()) || !aCol) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); // Check for a "label" attribute - this is valid on an // with a single implied column. @@ -512,7 +512,7 @@ nsTreeContentView::ToggleOpenState(int32_t aIndex) // We don't serialize content right here, since content might be generated // lazily. - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); if (row->IsOpen()) row->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::open, NS_LITERAL_STRING("false"), true); @@ -587,7 +587,7 @@ nsTreeContentView::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval) *_retval = true; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -612,7 +612,7 @@ nsTreeContentView::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval *_retval = true; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -635,7 +635,7 @@ nsTreeContentView::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAStri if (aRow < 0 || aRow >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -656,7 +656,7 @@ nsTreeContentView::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAStrin if (aRow < 0 || aRow >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aRow]; + Row* row = mRows[aRow].get(); nsIContent* realRow = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treerow); @@ -695,7 +695,7 @@ nsTreeContentView::GetItemAtIndex(int32_t aIndex, nsIDOMElement** _retval) if (aIndex < 0 || aIndex >= int32_t(mRows.Length())) return NS_ERROR_INVALID_ARG; - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); row->mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)_retval); return NS_OK; @@ -796,7 +796,7 @@ nsTreeContentView::AttributeChanged(nsIDocument* aDocument, else if (aElement->IsXULElement(nsGkAtoms::treeitem)) { int32_t index = FindContent(aElement); if (index >= 0) { - Row* row = mRows[index]; + Row* row = mRows[index].get(); if (aAttribute == nsGkAtoms::container) { bool isContainer = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container, @@ -917,7 +917,7 @@ nsTreeContentView::ContentInserted(nsIDocument *aDocument, if (aChild->IsXULElement(nsGkAtoms::treechildren)) { int32_t index = FindContent(aContainer); if (index >= 0) { - Row* row = mRows[index]; + Row* row = mRows[index].get(); row->SetEmpty(false); if (mBoxObject) mBoxObject->InvalidateRow(index); @@ -987,7 +987,7 @@ nsTreeContentView::ContentRemoved(nsIDocument *aDocument, if (aChild->IsXULElement(nsGkAtoms::treechildren)) { int32_t index = FindContent(aContainer); if (index >= 0) { - Row* row = mRows[index]; + Row* row = mRows[index].get(); row->SetEmpty(true); int32_t count = RemoveSubtree(index); // Invalidate also the row to update twisty. @@ -1033,7 +1033,7 @@ nsTreeContentView::NodeWillBeDestroyed(const nsINode* aNode) // Recursively serialize content, starting with aContent. void nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex, - int32_t* aIndex, nsTArray >& aRows) + int32_t* aIndex, nsTArray>& aRows) { // Don't allow non-XUL nodes. if (!aContent->IsXULElement()) @@ -1055,14 +1055,14 @@ nsTreeContentView::Serialize(nsIContent* aContent, int32_t aParentIndex, void nsTreeContentView::SerializeItem(nsIContent* aContent, int32_t aParentIndex, - int32_t* aIndex, nsTArray >& aRows) + int32_t* aIndex, nsTArray>& aRows) { if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, nsGkAtoms::_true, eCaseMatters)) return; - Row* row = new Row(aContent, aParentIndex); - aRows.AppendElement(row); + aRows.AppendElement(MakeUnique(aContent, aParentIndex)); + Row* row = aRows.LastElement().get(); if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container, nsGkAtoms::_true, eCaseMatters)) { @@ -1091,15 +1091,15 @@ nsTreeContentView::SerializeItem(nsIContent* aContent, int32_t aParentIndex, void nsTreeContentView::SerializeSeparator(nsIContent* aContent, int32_t aParentIndex, int32_t* aIndex, - nsTArray >& aRows) + nsTArray>& aRows) { if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, nsGkAtoms::_true, eCaseMatters)) return; - Row* row = new Row(aContent, aParentIndex); + auto row = MakeUnique(aContent, aParentIndex); row->SetSeparator(true); - aRows.AppendElement(row); + aRows.AppendElement(Move(row)); } void @@ -1143,7 +1143,7 @@ nsTreeContentView::GetIndexInSubtree(nsIContent* aContainer, int32_t nsTreeContentView::EnsureSubtree(int32_t aIndex) { - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); nsIContent* child; child = nsTreeUtils::GetImmediateChild(row->mContent, nsGkAtoms::treechildren); @@ -1151,14 +1151,17 @@ nsTreeContentView::EnsureSubtree(int32_t aIndex) return 0; } - AutoTArray, 8> rows; + AutoTArray, 8> rows; int32_t index = 0; Serialize(child, aIndex, &index, rows); - // We can't use InsertElementsAt since the destination can't steal - // ownership from its const source argument. + // Insert |rows| into |mRows| at position |aIndex|, by first creating empty + // UniquePtr entries and then Move'ing |rows|'s entries into them. (Note + // that we can't simply use InsertElementsAt with an array argument, since + // the destination can't steal ownership from its const source argument.) + UniquePtr* newRows = mRows.InsertElementsAt(aIndex + 1, + rows.Length()); for (nsTArray::index_type i = 0; i < rows.Length(); i++) { - nsAutoPtr* newRow = mRows.InsertElementAt(aIndex + i + 1); - *newRow = rows[i]; + newRows[i] = Move(rows[i]); } int32_t count = rows.Length(); @@ -1175,7 +1178,7 @@ nsTreeContentView::EnsureSubtree(int32_t aIndex) int32_t nsTreeContentView::RemoveSubtree(int32_t aIndex) { - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); int32_t count = row->mSubtreeSize; mRows.RemoveElementsAt(aIndex + 1, count); @@ -1226,7 +1229,7 @@ nsTreeContentView::InsertRowFor(nsIContent* aParent, nsIContent* aChild) int32_t nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex, nsIContent* aContent) { - AutoTArray, 8> rows; + AutoTArray, 8> rows; if (aContent->IsXULElement(nsGkAtoms::treeitem)) { SerializeItem(aContent, aParentIndex, &aIndex, rows); } else if (aContent->IsXULElement(nsGkAtoms::treeseparator)) { @@ -1235,11 +1238,10 @@ nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex, nsIContent* a // We can't use InsertElementsAt since the destination can't steal // ownership from its const source argument. - for (nsTArray::index_type i = 0; i < rows.Length(); i++) { - nsAutoPtr* newRow = mRows.InsertElementAt(aParentIndex + aIndex + i + 1); - *newRow = rows[i]; - } int32_t count = rows.Length(); + for (nsTArray::index_type i = 0; i < size_t(count); i++) { + mRows.InsertElementAt(aParentIndex + aIndex + i + 1, Move(rows[i])); + } UpdateSubtreeSizes(aParentIndex, count); @@ -1253,7 +1255,7 @@ nsTreeContentView::InsertRow(int32_t aParentIndex, int32_t aIndex, nsIContent* a int32_t nsTreeContentView::RemoveRow(int32_t aIndex) { - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); int32_t count = row->mSubtreeSize + 1; int32_t parentIndex = row->mParentIndex; @@ -1282,7 +1284,7 @@ nsTreeContentView::ClearRows() void nsTreeContentView::OpenContainer(int32_t aIndex) { - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); row->SetOpen(true); int32_t count = EnsureSubtree(aIndex); @@ -1295,7 +1297,7 @@ nsTreeContentView::OpenContainer(int32_t aIndex) void nsTreeContentView::CloseContainer(int32_t aIndex) { - Row* row = mRows[aIndex]; + Row* row = mRows[aIndex].get(); row->SetOpen(false); int32_t count = RemoveSubtree(aIndex); @@ -1321,7 +1323,7 @@ void nsTreeContentView::UpdateSubtreeSizes(int32_t aParentIndex, int32_t count) { while (aParentIndex >= 0) { - Row* row = mRows[aParentIndex]; + Row* row = mRows[aParentIndex].get(); row->mSubtreeSize += count; aParentIndex = row->mParentIndex; } @@ -1332,7 +1334,7 @@ nsTreeContentView::UpdateParentIndexes(int32_t aIndex, int32_t aSkip, int32_t aC { int32_t count = mRows.Length(); for (int32_t i = aIndex + aSkip; i < count; i++) { - Row* row = mRows[i]; + Row* row = mRows[i].get(); if (row->mParentIndex > aIndex) { row->mParentIndex += aCount; } diff --git a/layout/xul/tree/nsTreeContentView.h b/layout/xul/tree/nsTreeContentView.h index d0898c9f2e..8f85b65074 100644 --- a/layout/xul/tree/nsTreeContentView.h +++ b/layout/xul/tree/nsTreeContentView.h @@ -16,6 +16,7 @@ #include "nsITreeContentView.h" #include "nsITreeSelection.h" #include "mozilla/Attributes.h" +#include "mozilla/UniquePtr.h" class nsIDocument; class Row; @@ -53,13 +54,13 @@ class nsTreeContentView final : public nsINativeTreeView, // Recursive methods which deal with serializing of nested content. void Serialize(nsIContent* aContent, int32_t aParentIndex, int32_t* aIndex, - nsTArray >& aRows); + nsTArray>& aRows); void SerializeItem(nsIContent* aContent, int32_t aParentIndex, - int32_t* aIndex, nsTArray >& aRows); + int32_t* aIndex, nsTArray>& aRows); void SerializeSeparator(nsIContent* aContent, int32_t aParentIndex, - int32_t* aIndex, nsTArray >& aRows); + int32_t* aIndex, nsTArray>& aRows); void GetIndexInSubtree(nsIContent* aContainer, nsIContent* aContent, int32_t* aResult); @@ -95,7 +96,7 @@ class nsTreeContentView final : public nsINativeTreeView, nsCOMPtr mRoot; nsCOMPtr mBody; nsIDocument* mDocument; // WEAK - nsTArray > mRows; + nsTArray> mRows; }; #endif // nsTreeContentView_h__ diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 2439b57b54..ba2c497dfc 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -549,9 +549,6 @@ pref("media.video-queue.send-to-compositor-size", 9999); // Whether to disable the video stats to prevent fingerprinting pref("media.video_stats.enabled", true); -// Whether to enable the audio writing APIs on the audio element -pref("media.audio_data.enabled", false); - // Weather we allow AMD switchable graphics pref("layers.amd-switchable-gfx.enabled", true); @@ -624,10 +621,10 @@ pref("apz.touch_start_tolerance", "0.2222222"); // 0.2222222 came from 1.0/4.5 pref("apz.touch_move_tolerance", "0.0"); pref("apz.velocity_bias", "1.0"); pref("apz.velocity_relevance_time_ms", 150); -pref("apz.x_stationary_size_multiplier", "3.0"); -pref("apz.y_stationary_size_multiplier", "3.5"); pref("apz.x_skate_highmem_adjust", "0.0"); pref("apz.y_skate_highmem_adjust", "0.0"); +pref("apz.x_stationary_size_multiplier", "3.0"); +pref("apz.y_stationary_size_multiplier", "3.5"); pref("apz.zoom_animation_duration_ms", 250); #ifdef XP_MACOSX @@ -652,8 +649,7 @@ pref("gfx.hidpi.enabled", 2); #endif #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) -// Containerless scrolling for root frames does not yet pass tests on Android -// or B2G. +// Use containerless scrolling for now on desktop. pref("layout.scroll.root-frame-containers", false); #endif @@ -897,6 +893,7 @@ sticky_pref("devtools.chrome.enabled", false); // Disable remote debugging protocol logging pref("devtools.debugger.log", false); pref("devtools.debugger.log.verbose", false); + // Disable remote debugging connections #ifdef MOZ_DEV_EDITION sticky_pref("devtools.debugger.remote-enabled", true); @@ -1244,6 +1241,9 @@ pref("nglayout.debug.paint_flashing_chrome", false); // BasicLayers (other layer managers always update the entire widget area) pref("nglayout.debug.widget_update_flashing", false); +// Enable/disable display list invalidation logging --- useful for debugging. +pref("nglayout.debug.invalidation", false); + // Whether frame visibility tracking is enabled globally. pref("layout.framevisibility.enabled", true); @@ -1319,7 +1319,7 @@ pref("print.print_edge_right", 0); pref("print.print_edge_bottom", 0); // Print via the parent process. This is only used when e10s is enabled. -#if defined(XP_WIN) +#if defined(XP_WIN) && defined(NIGHTLY_BUILD) pref("print.print_via_parent", true); #else pref("print.print_via_parent", false); @@ -1330,9 +1330,6 @@ pref("print.print_via_parent", false); // in a document. pref("extensions.spellcheck.inline.max-misspellings", 500); -// Predefined convenience pref for overriding the dictionary -pref("spellchecker.dictionary.override", ""); - // Prefs used by libeditor. Prefs specific to seamonkey composer // belong in comm-central/editor/ui/composer.js @@ -1396,6 +1393,10 @@ pref("dom.forms.autocomplete.experimental", false); // Enables requestAutocomplete DOM API on forms. pref("dom.forms.requestAutocomplete", false); +#ifdef NIGHTLY_BUILD +pref("dom.input.dirpicker", true); +#endif + // Enables system messages and activities pref("dom.sysmsg.enabled", false); @@ -1492,15 +1493,15 @@ pref("javascript.options.mem.gc_max_empty_chunk_count", 30); pref("javascript.options.showInConsole", false); -pref("javascript.options.throw_on_debuggee_would_run", false); -pref("javascript.options.dump_stack_on_debuggee_would_run", false); - #ifdef NIGHTLY_BUILD pref("javascript.options.shared_memory", true); #else pref("javascript.options.shared_memory", false); #endif +pref("javascript.options.throw_on_debuggee_would_run", false); +pref("javascript.options.dump_stack_on_debuggee_would_run", false); + // advanced prefs pref("advanced.mailftp", false); pref("image.animation_mode", "normal"); @@ -2187,7 +2188,6 @@ pref("network.cookie.cookieBehavior", 0); // Keep the old default of accep #endif pref("network.cookie.thirdparty.sessionOnly", false); pref("network.cookie.lifetimePolicy", 0); // 0-accept, 2-acceptForSession, 3-acceptForNDays -pref("network.cookie.alwaysAcceptSessionCookies", false); pref("network.cookie.prefsMigrated", false); pref("network.cookie.lifetime.days", 90); @@ -2623,9 +2623,6 @@ pref("layout.css.isolation.enabled", true); // Is support for CSS Filters enabled? pref("layout.css.filters.enabled", true); -// Is support for scroll-snap enabled? -pref("layout.css.scroll-snap.enabled", false); - // Set the threshold distance in CSS pixels below which scrolling will snap to // an edge, when scroll snapping is set to "proximity". pref("layout.css.scroll-snap.proximity-threshold", 200); @@ -2790,6 +2787,9 @@ pref("layout.css.scroll-behavior.spring-constant", "250.0"); // at the greatest speed without overshooting. pref("layout.css.scroll-behavior.damping-ratio", "1.0"); +// Is support for scroll-snap enabled? +pref("layout.css.scroll-snap.enabled", true); + // Is support for document.fonts enabled? // // Don't enable the pref for the CSS Font Loading API until bug 1072101 is @@ -2878,7 +2878,6 @@ pref("editor.positioning.offset", 0); pref("dom.use_watchdog", true); pref("dom.max_chrome_script_run_time", 20); -pref("dom.max_child_script_run_time", 10); pref("dom.max_script_run_time", 10); // Stop all scripts in a compartment when the "Stop script" button on the @@ -3916,22 +3915,22 @@ pref("font.name.serif.zh-CN", "Times"); pref("font.name.sans-serif.zh-CN", "Helvetica"); pref("font.name.monospace.zh-CN", "Courier"); pref("font.name-list.serif.zh-CN", "Times,STSong,Heiti SC"); -pref("font.name-list.sans-serif.zh-CN", "Helvetica,STHeiti,Heiti SC"); -pref("font.name-list.monospace.zh-CN", "Courier,STHeiti,Heiti SC"); +pref("font.name-list.sans-serif.zh-CN", "Helvetica,PingFang SC,STHeiti,Heiti SC"); +pref("font.name-list.monospace.zh-CN", "Courier,PingFang SC,STHeiti,Heiti SC"); pref("font.name.serif.zh-TW", "Times"); pref("font.name.sans-serif.zh-TW", "Helvetica"); pref("font.name.monospace.zh-TW", "Courier"); pref("font.name-list.serif.zh-TW", "Times,LiSong Pro,Heiti TC"); -pref("font.name-list.sans-serif.zh-TW", "Helvetica,Heiti TC,LiHei Pro"); -pref("font.name-list.monospace.zh-TW", "Courier,Heiti TC,LiHei Pro"); +pref("font.name-list.sans-serif.zh-TW", "Helvetica,PingFang TC,Heiti TC,LiHei Pro"); +pref("font.name-list.monospace.zh-TW", "Courier,PingFang TC,Heiti TC,LiHei Pro"); pref("font.name.serif.zh-HK", "Times"); pref("font.name.sans-serif.zh-HK", "Helvetica"); pref("font.name.monospace.zh-HK", "Courier"); pref("font.name-list.serif.zh-HK", "Times,LiSong Pro,Heiti TC"); -pref("font.name-list.sans-serif.zh-HK", "Helvetica,Heiti TC,LiHei Pro"); -pref("font.name-list.monospace.zh-HK", "Courier,Heiti TC,LiHei Pro"); +pref("font.name-list.sans-serif.zh-HK", "Helvetica,PingFang TC,Heiti TC,LiHei Pro"); +pref("font.name-list.monospace.zh-HK", "Courier,PingFang TC,Heiti TC,LiHei Pro"); // XP_MACOSX changes to default font sizes pref("font.minimum-size.th", 10); @@ -4050,12 +4049,16 @@ pref("print.print_paper_size", 0); // around the content of the page for Print Preview only pref("print.print_extra_margin", 0); // twips -// CSSOM-View scroll-behavior smooth scrolling requires the C++ APZC +// CSSOM-View scroll-behavior smooth scrolling and scroll snap requires the C++ APZC +#ifdef MOZ_ANDROID_APZ +pref("layout.css.scroll-behavior.enabled", true); +pref("layout.css.scroll-behavior.property-enabled", true); +pref("layout.css.scroll-snap.enabled", true); +#else pref("layout.css.scroll-behavior.enabled", false); pref("layout.css.scroll-behavior.property-enabled", false); - -// CSS Scroll Snapping requires the C++ APZC pref("layout.css.scroll-snap.enabled", false); +#endif /* PostScript print module prefs */ // pref("print.postscript.enabled", true); @@ -4146,14 +4149,78 @@ pref("font.name.monospace.ko", "monospace"); pref("font.name.serif.th", "serif"); pref("font.name.sans-serif.th", "sans-serif"); -pref("font.minimum-size.th", 13); pref("font.name.monospace.th", "monospace"); +pref("font.minimum-size.th", 13); + +pref("font.name.serif.x-armn", "serif"); +pref("font.name.sans-serif.x-armn", "sans-serif"); +pref("font.name.monospace.x-armn", "monospace"); + +pref("font.name.serif.x-beng", "serif"); +pref("font.name.sans-serif.x-beng", "sans-serif"); +pref("font.name.monospace.x-beng", "monospace"); + +pref("font.name.serif.x-cans", "serif"); +pref("font.name.sans-serif.x-cans", "sans-serif"); +pref("font.name.monospace.x-cans", "monospace"); pref("font.name.serif.x-cyrillic", "serif"); pref("font.name.sans-serif.x-cyrillic", "sans-serif"); pref("font.name.monospace.x-cyrillic", "monospace"); pref("font.size.fixed.x-cyrillic", 12); +pref("font.name.serif.x-devanagari", "serif"); +pref("font.name.sans-serif.x-devanagari", "sans-serif"); +pref("font.name.monospace.x-devanagari", "monospace"); + +pref("font.name.serif.x-ethi", "serif"); +pref("font.name.sans-serif.x-ethi", "sans-serif"); +pref("font.name.monospace.x-ethi", "monospace"); + +pref("font.name.serif.x-geor", "serif"); +pref("font.name.sans-serif.x-geor", "sans-serif"); +pref("font.name.monospace.x-geor", "monospace"); + +pref("font.name.serif.x-gujr", "serif"); +pref("font.name.sans-serif.x-gujr", "sans-serif"); +pref("font.name.monospace.x-gujr", "monospace"); + +pref("font.name.serif.x-guru", "serif"); +pref("font.name.sans-serif.x-guru", "sans-serif"); +pref("font.name.monospace.x-guru", "monospace"); + +pref("font.name.serif.x-khmr", "serif"); +pref("font.name.sans-serif.x-khmr", "sans-serif"); +pref("font.name.monospace.x-khmr", "monospace"); + +pref("font.name.serif.x-knda", "serif"); +pref("font.name.sans-serif.x-knda", "sans-serif"); +pref("font.name.monospace.x-knda", "monospace"); + +pref("font.name.serif.x-mlym", "serif"); +pref("font.name.sans-serif.x-mlym", "sans-serif"); +pref("font.name.monospace.x-mlym", "monospace"); + +pref("font.name.serif.x-orya", "serif"); +pref("font.name.sans-serif.x-orya", "sans-serif"); +pref("font.name.monospace.x-orya", "monospace"); + +pref("font.name.serif.x-sinh", "serif"); +pref("font.name.sans-serif.x-sinh", "sans-serif"); +pref("font.name.monospace.x-sinh", "monospace"); + +pref("font.name.serif.x-tamil", "serif"); +pref("font.name.sans-serif.x-tamil", "sans-serif"); +pref("font.name.monospace.x-tamil", "monospace"); + +pref("font.name.serif.x-telu", "serif"); +pref("font.name.sans-serif.x-telu", "sans-serif"); +pref("font.name.monospace.x-telu", "monospace"); + +pref("font.name.serif.x-tibt", "serif"); +pref("font.name.sans-serif.x-tibt", "sans-serif"); +pref("font.name.monospace.x-tibt", "monospace"); + pref("font.name.serif.x-unicode", "serif"); pref("font.name.sans-serif.x-unicode", "sans-serif"); pref("font.name.monospace.x-unicode", "monospace"); @@ -4168,8 +4235,6 @@ pref("font.name.serif.zh-CN", "serif"); pref("font.name.sans-serif.zh-CN", "sans-serif"); pref("font.name.monospace.zh-CN", "monospace"); -// ming_uni.ttf (HKSCS-2001) -// http://www.info.gov.hk/digital21/eng/hkscs/download/uime.exe pref("font.name.serif.zh-HK", "serif"); pref("font.name.sans-serif.zh-HK", "sans-serif"); pref("font.name.monospace.zh-HK", "monospace"); @@ -4314,68 +4379,70 @@ pref("font.name.monospace.x-math", "Fira Mono"); pref("font.name.serif.el", "Droid Serif"); // not Charis SIL Compact, only has a few Greek chars pref("font.name.sans-serif.el", "Clear Sans"); pref("font.name.monospace.el", "Droid Sans Mono"); +pref("font.name-list.serif.el", "Noto Serif"); pref("font.name-list.sans-serif.el", "Clear Sans, Roboto, Droid Sans"); pref("font.name.serif.he", "Droid Serif"); pref("font.name.sans-serif.he", "Clear Sans"); pref("font.name.monospace.he", "Droid Sans Mono"); +pref("font.name-list.serif.he", "Noto Serif"); pref("font.name-list.sans-serif.he", "Droid Sans Hebrew, Clear Sans, Droid Sans"); pref("font.name.serif.ja", "Charis SIL Compact"); pref("font.name.sans-serif.ja", "Clear Sans"); pref("font.name.monospace.ja", "MotoyaLMaru"); -pref("font.name-list.serif.ja", "Droid Serif"); +pref("font.name-list.serif.ja", "Noto Serif, Droid Serif"); pref("font.name-list.sans-serif.ja", "Clear Sans, Roboto, Droid Sans, MotoyaLMaru, MotoyaLCedar, Noto Sans JP, Droid Sans Japanese"); pref("font.name-list.monospace.ja", "MotoyaLMaru, MotoyaLCedar, Droid Sans Mono"); pref("font.name.serif.ko", "Charis SIL Compact"); pref("font.name.sans-serif.ko", "Clear Sans"); pref("font.name.monospace.ko", "Droid Sans Mono"); -pref("font.name-list.serif.ko", "Droid Serif, HYSerif"); +pref("font.name-list.serif.ko", "Noto Serif, Droid Serif, HYSerif"); pref("font.name-list.sans-serif.ko", "SmartGothic, NanumGothic, Noto Sans KR, DroidSansFallback, Droid Sans Fallback"); pref("font.name.serif.th", "Charis SIL Compact"); pref("font.name.sans-serif.th", "Clear Sans"); pref("font.name.monospace.th", "Droid Sans Mono"); -pref("font.name-list.serif.th", "Droid Serif"); +pref("font.name-list.serif.th", "Noto Serif, Droid Serif"); pref("font.name-list.sans-serif.th", "Droid Sans Thai, Clear Sans, Droid Sans"); pref("font.name.serif.x-cyrillic", "Charis SIL Compact"); pref("font.name.sans-serif.x-cyrillic", "Clear Sans"); pref("font.name.monospace.x-cyrillic", "Droid Sans Mono"); -pref("font.name-list.serif.x-cyrillic", "Droid Serif"); +pref("font.name-list.serif.x-cyrillic", "Noto Serif, Droid Serif"); pref("font.name-list.sans-serif.x-cyrillic", "Clear Sans, Roboto, Droid Sans"); pref("font.name.serif.x-unicode", "Charis SIL Compact"); pref("font.name.sans-serif.x-unicode", "Clear Sans"); pref("font.name.monospace.x-unicode", "Droid Sans Mono"); -pref("font.name-list.serif.x-unicode", "Droid Serif"); +pref("font.name-list.serif.x-unicode", "Noto Serif, Droid Serif"); pref("font.name-list.sans-serif.x-unicode", "Clear Sans, Roboto, Droid Sans"); pref("font.name.serif.x-western", "Charis SIL Compact"); pref("font.name.sans-serif.x-western", "Clear Sans"); pref("font.name.monospace.x-western", "Droid Sans Mono"); -pref("font.name-list.serif.x-western", "Droid Serif"); +pref("font.name-list.serif.x-western", "Noto Serif, Droid Serif"); pref("font.name-list.sans-serif.x-western", "Clear Sans, Roboto, Droid Sans"); pref("font.name.serif.zh-CN", "Charis SIL Compact"); pref("font.name.sans-serif.zh-CN", "Clear Sans"); pref("font.name.monospace.zh-CN", "Droid Sans Mono"); -pref("font.name-list.serif.zh-CN", "Droid Serif, Droid Sans Fallback"); +pref("font.name-list.serif.zh-CN", "Noto Serif, Droid Serif, Droid Sans Fallback"); pref("font.name-list.sans-serif.zh-CN", "Roboto, Droid Sans, Noto Sans SC, Droid Sans Fallback"); pref("font.name-list.monospace.zh-CN", "Droid Sans Fallback"); pref("font.name.serif.zh-HK", "Charis SIL Compact"); pref("font.name.sans-serif.zh-HK", "Clear Sans"); pref("font.name.monospace.zh-HK", "Droid Sans Mono"); -pref("font.name-list.serif.zh-HK", "Droid Serif, Droid Sans Fallback"); +pref("font.name-list.serif.zh-HK", "Noto Serif, Droid Serif, Droid Sans Fallback"); pref("font.name-list.sans-serif.zh-HK", "Roboto, Droid Sans, Noto Sans TC, Noto Sans SC, Droid Sans Fallback"); pref("font.name-list.monospace.zh-HK", "Droid Sans Fallback"); pref("font.name.serif.zh-TW", "Charis SIL Compact"); pref("font.name.sans-serif.zh-TW", "Clear Sans"); pref("font.name.monospace.zh-TW", "Droid Sans Mono"); -pref("font.name-list.serif.zh-TW", "Droid Serif, Droid Sans Fallback"); +pref("font.name-list.serif.zh-TW", "Noto Serif, Droid Serif, Droid Sans Fallback"); pref("font.name-list.sans-serif.zh-TW", "Roboto, Droid Sans, Noto Sans TC, Noto Sans SC, Droid Sans Fallback"); pref("font.name-list.monospace.zh-TW", "Droid Sans Fallback"); @@ -5271,16 +5338,21 @@ pref("layout.accessiblecaret.timeout_ms", 3000); // or long tap events does not fired by APZ. pref("layout.accessiblecaret.use_long_tap_injector", true); -// Use AccessibleCaret default behaviours. -pref("layout.accessiblecaret.extendedvisibility", false); - // By default, carets become tilt only when they are overlapping. pref("layout.accessiblecaret.always_tilt", false); +// By default, carets always show when scrolling (either panning for zooming) +// the page. +pref("layout.accessiblecaret.always_show_when_scrolling", true); + // Selection change notifications generated by Javascript hide // AccessibleCarets and close UI interaction by default. pref("layout.accessiblecaret.allow_script_change_updates", false); +// Allow one caret to be dragged across the other caret without any limitation. +// This matches the built-in convention for all desktop platforms. +pref("layout.accessiblecaret.allow_dragging_across_other_caret", true); + // Optionally provide haptic feedback on longPress selection events. pref("layout.accessiblecaret.hapticfeedback", false); @@ -5334,6 +5406,7 @@ pref("dom.presentation.tcp_server.debug", false); pref("dom.presentation.discovery.enabled", false); pref("dom.presentation.discovery.timeout_ms", 10000); pref("dom.presentation.discoverable", false); +pref("dom.presentation.session_transport.data_channel.enable", false); #ifdef XP_MACOSX // Use raw ICU instead of CoreServices API in Unicode collation @@ -5439,27 +5512,6 @@ pref("media.gmp-manager.certs.2.commonName", "aus5.mozilla.org"); // If this pref is disabled, we will never show a reader mode icon in the toolbar. pref("reader.parse-on-load.enabled", true); -// Force-enables reader mode parsing, even on low-memory platforms, where it -// is disabled by default. -pref("reader.parse-on-load.force-enabled", false); - -// The default relative font size in reader mode (1-5) -pref("reader.font_size", 3); - -// The default color scheme in reader mode (light, dark, auto) -// auto = color automatically adjusts according to ambient light level -pref("reader.color_scheme", "auto"); - -// The font type in reader (sans-serif, serif) -pref("reader.font_type", "sans-serif"); - -// Whether or not the user has interacted with the reader mode toolbar. -// This is used to show a first-launch tip in reader mode. -pref("reader.has_used_toolbar", false); -// Whether or not to perform reader mode article parsing on page load. -// If this pref is disabled, we will never show a reader mode icon in the toolbar. -pref("reader.parse-on-load.enabled", true); - // After what size document we don't bother running Readability on it // because it'd slow things down too much pref("reader.parse-node-limit", 3000); diff --git a/netwerk/base/nsAutodialWin.cpp b/netwerk/base/nsAutodialWin.cpp index b18ae50fd3..da7525c9ff 100644 --- a/netwerk/base/nsAutodialWin.cpp +++ b/netwerk/base/nsAutodialWin.cpp @@ -14,6 +14,7 @@ #include "nsAutodialWin.h" #include "mozilla/Logging.h" #include "nsWindowsHelpers.h" +#include "mozilla/Telemetry.h" #define AUTODIAL_DEFAULT AUTODIAL_NEVER @@ -187,6 +188,9 @@ int nsAutodial::QueryAutodialBehavior() } } +// only do telemetry once per session +static bool reportedAutoDial = false; + // If the RAS autodial service is running, use it. Otherwise, dial // the default RAS connection. There are two possible RAS dialogs: // one that dials a single entry, and one that lets the user choose which @@ -267,6 +271,10 @@ nsresult nsAutodial::DialDefault(const char16_t* hostName) return NS_ERROR_FAILURE; // don't retry } + if (!reportedAutoDial) { + reportedAutoDial = true; + mozilla::Telemetry::Accumulate(mozilla::Telemetry::NETWORK_AUTODIAL, true); + } LOGD(("Autodial: RAS dialup connection successful.")); } @@ -298,6 +306,10 @@ nsresult nsAutodial::DialDefault(const char16_t* hostName) return NS_ERROR_FAILURE; // don't retry } + if (!reportedAutoDial) { + reportedAutoDial = true; + mozilla::Telemetry::Accumulate(mozilla::Telemetry::NETWORK_AUTODIAL, true); + } LOGD(("Autodial: RAS dialup connection successful.")); } } diff --git a/netwerk/base/nsChannelClassifier.cpp b/netwerk/base/nsChannelClassifier.cpp index c1e97e72db..b6c0ba3100 100644 --- a/netwerk/base/nsChannelClassifier.cpp +++ b/netwerk/base/nsChannelClassifier.cpp @@ -7,6 +7,7 @@ #include "nsChannelClassifier.h" #include "mozIThirdPartyUtil.h" +#include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsICacheEntry.h" #include "nsICachingChannel.h" @@ -313,6 +314,18 @@ nsChannelClassifier::StartInternal() NS_ENSURE_SUCCESS(rv, rv); if (hasFlags) return NS_ERROR_UNEXPECTED; + // Skip whitelisted hostnames. + nsAutoCString whitelisted; + Preferences::GetCString("urlclassifier.skipHostnames", &whitelisted); + if (!whitelisted.IsEmpty()) { + ToLowerCase(whitelisted); + LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s", + this, whitelisted.get())); + if (IsHostnameWhitelisted(uri, whitelisted)) { + return NS_ERROR_UNEXPECTED; + } + } + nsCOMPtr uriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv); if (rv == NS_ERROR_FACTORY_NOT_REGISTERED || @@ -372,6 +385,30 @@ nsChannelClassifier::StartInternal() return NS_OK; } +bool +nsChannelClassifier::IsHostnameWhitelisted(nsIURI *aUri, + const nsACString &aWhitelisted) +{ + nsAutoCString host; + nsresult rv = aUri->GetHost(host); + if (NS_FAILED(rv) || host.IsEmpty()) { + return false; + } + ToLowerCase(host); + + nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ','); + while (tokenizer.hasMoreTokens()) { + const nsCSubstring& token = tokenizer.nextToken(); + if (token.Equals(host)) { + LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)", + this, host.get())); + return true; + } + } + + return false; +} + // Note in the cache entry that this URL was classified, so that future // cached loads don't need to be checked. void @@ -385,6 +422,17 @@ nsChannelClassifier::MarkEntryClassified(nsresult status) return; } + if (LOG_ENABLED()) { + nsAutoCString errorName; + mozilla::GetErrorName(status, errorName); + nsCOMPtr uri; + mChannel->GetURI(getter_AddRefs(uri)); + nsAutoCString spec; + uri->GetAsciiSpec(spec); + LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s", + errorName.get(), spec.get())); + } + nsCOMPtr cachingChannel = do_QueryInterface(mChannel); if (!cachingChannel) { return; diff --git a/netwerk/base/nsChannelClassifier.h b/netwerk/base/nsChannelClassifier.h index 8a047c2193..fef275bb1e 100644 --- a/netwerk/base/nsChannelClassifier.h +++ b/netwerk/base/nsChannelClassifier.h @@ -44,6 +44,8 @@ private: nsresult StartInternal(); // Helper function to check a tracking URI against the whitelist nsresult IsTrackerWhitelisted(); + // Helper function to check a URI against the hostname whitelist + bool IsHostnameWhitelisted(nsIURI *aUri, const nsACString &aWhitelisted); // Checks that the channel was loaded by the URI currently loaded in aDoc static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel); diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp index cff38d324a..6684f766bb 100644 --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -323,6 +323,8 @@ nsFileStreamBase::MaybeOpen(nsIFile* aFile, int32_t aIoFlags, mOpenParams.localFile = aFile; + // Following call open() at main thread. + // Main thread might be blocked, while open a remote file. return DoOpen(); } @@ -432,19 +434,23 @@ nsFileInputStream::Open(nsIFile* aFile, int32_t aIOFlags, int32_t aPerm) rv = MaybeOpen(aFile, aIOFlags, aPerm, mBehaviorFlags & nsIFileInputStream::DEFER_OPEN); + if (NS_FAILED(rv)) return rv; - if (mBehaviorFlags & DELETE_ON_CLOSE) { - // POSIX compatible filesystems allow a file to be unlinked while a - // file descriptor is still referencing the file. since we've already - // opened the file descriptor, we'll try to remove the file. if that - // fails, then we'll just remember the nsIFile and remove it after we - // close the file descriptor. - rv = aFile->Remove(false); - if (NS_SUCCEEDED(rv)) { - // No need to remove it later. Clear the flag. - mBehaviorFlags &= ~DELETE_ON_CLOSE; - } + // if defer open is set, do not remove the file here. + // remove the file while Close() is called. + if ((mBehaviorFlags & DELETE_ON_CLOSE) && + !(mBehaviorFlags & nsIFileInputStream::DEFER_OPEN)) { + // POSIX compatible filesystems allow a file to be unlinked while a + // file descriptor is still referencing the file. since we've already + // opened the file descriptor, we'll try to remove the file. if that + // fails, then we'll just remember the nsIFile and remove it after we + // close the file descriptor. + rv = aFile->Remove(false); + if (NS_SUCCEEDED(rv)) { + // No need to remove it later. Clear the flag. + mBehaviorFlags &= ~DELETE_ON_CLOSE; + } } return NS_OK; @@ -514,9 +520,6 @@ nsFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) NS_IMETHODIMP nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult) { - nsresult rv = DoPendingOpen(); - NS_ENSURE_SUCCESS(rv, rv); - if (!mLineBuffer) { mLineBuffer = new nsLineBuffer; } @@ -525,11 +528,19 @@ nsFileInputStream::ReadLine(nsACString& aLine, bool* aResult) NS_IMETHODIMP nsFileInputStream::Seek(int32_t aWhence, int64_t aOffset) +{ + return SeekInternal(aWhence, aOffset); +} + +nsresult +nsFileInputStream::SeekInternal(int32_t aWhence, int64_t aOffset, bool aClearBuf) { nsresult rv = DoPendingOpen(); NS_ENSURE_SUCCESS(rv, rv); - mLineBuffer = nullptr; + if (aClearBuf) { + mLineBuffer = nullptr; + } if (!mFD) { if (mBehaviorFlags & REOPEN_ON_REWIND) { rv = Open(mFile, mIOFlags, mPerm); @@ -699,19 +710,28 @@ nsPartialFileInputStream::Init(nsIFile* aFile, uint64_t aStart, nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm, aBehaviorFlags); + + // aFile is a partial file, it must exist. NS_ENSURE_SUCCESS(rv, rv); - return nsFileInputStream::Seek(NS_SEEK_SET, mStart); + mDeferredSeek = true; + + return rv; } NS_IMETHODIMP nsPartialFileInputStream::Tell(int64_t *aResult) { int64_t tell = 0; - nsresult rv = nsFileInputStream::Tell(&tell); - if (NS_SUCCEEDED(rv)) { - *aResult = tell - mStart; - } + + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = nsFileInputStream::Tell(&tell); + NS_ENSURE_SUCCESS(rv, rv); + + + *aResult = tell - mStart; return rv; } @@ -719,16 +739,23 @@ NS_IMETHODIMP nsPartialFileInputStream::Available(uint64_t* aResult) { uint64_t available = 0; - nsresult rv = nsFileInputStream::Available(&available); - if (NS_SUCCEEDED(rv)) { - *aResult = TruncateSize(available); - } + + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = nsFileInputStream::Available(&available); + NS_ENSURE_SUCCESS(rv, rv); + + *aResult = TruncateSize(available); return rv; } NS_IMETHODIMP nsPartialFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult) { + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t readsize = (uint32_t) TruncateSize(aCount); if (readsize == 0 && mBehaviorFlags & CLOSE_ON_EOF) { Close(); @@ -736,16 +763,19 @@ nsPartialFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult) return NS_OK; } - nsresult rv = nsFileInputStream::Read(aBuf, readsize, aResult); - if (NS_SUCCEEDED(rv)) { - mPosition += readsize; - } + rv = nsFileInputStream::Read(aBuf, readsize, aResult); + NS_ENSURE_SUCCESS(rv, rv); + + mPosition += readsize; return rv; } NS_IMETHODIMP nsPartialFileInputStream::Seek(int32_t aWhence, int64_t aOffset) { + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + int64_t offset; switch (aWhence) { case NS_SEEK_SET: @@ -765,10 +795,10 @@ nsPartialFileInputStream::Seek(int32_t aWhence, int64_t aOffset) return NS_ERROR_INVALID_ARG; } - nsresult rv = nsFileInputStream::Seek(NS_SEEK_SET, offset); - if (NS_SUCCEEDED(rv)) { - mPosition = offset - mStart; - } + rv = nsFileInputStream::Seek(NS_SEEK_SET, offset); + NS_ENSURE_SUCCESS(rv, rv); + + mPosition = offset - mStart; return rv; } @@ -828,6 +858,19 @@ nsPartialFileInputStream::Deserialize( return NS_SUCCEEDED(nsFileInputStream::Seek(NS_SEEK_SET, mStart)); } +nsresult +nsPartialFileInputStream::DoPendingSeek() +{ + if (!mDeferredSeek) { + return NS_OK; + } + + mDeferredSeek = false; + + // This is the first time to open the file, don't clear mLinebuffer. + // mLineBuffer might be already initialized by ReadLine(). + return nsFileInputStream::SeekInternal(NS_SEEK_SET, mStart, false); +} //////////////////////////////////////////////////////////////////////////////// // nsFileOutputStream diff --git a/netwerk/base/nsFileStreams.h b/netwerk/base/nsFileStreams.h index ad2c3c3b6a..00a020012c 100644 --- a/netwerk/base/nsFileStreams.h +++ b/netwerk/base/nsFileStreams.h @@ -143,6 +143,8 @@ protected: Close(); } + nsresult SeekInternal(int32_t aWhence, int64_t aOffset, bool aClearBuf=true); + nsAutoPtr > mLineBuffer; /** @@ -184,7 +186,7 @@ public: NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM nsPartialFileInputStream() - : mStart(0), mLength(0), mPosition(0) + : mStart(0), mLength(0), mPosition(0), mDeferredSeek(false) { } NS_IMETHOD Tell(int64_t *aResult) override; @@ -199,6 +201,8 @@ protected: ~nsPartialFileInputStream() { } + inline nsresult DoPendingSeek(); + private: uint64_t TruncateSize(uint64_t aSize) { return std::min(mLength - mPosition, aSize); @@ -207,6 +211,7 @@ private: uint64_t mStart; uint64_t mLength; uint64_t mPosition; + bool mDeferredSeek; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/netwerk/base/nsIMIMEInputStream.idl b/netwerk/base/nsIMIMEInputStream.idl index 73b1e5fb67..82992d939b 100644 --- a/netwerk/base/nsIMIMEInputStream.idl +++ b/netwerk/base/nsIMIMEInputStream.idl @@ -39,4 +39,9 @@ interface nsIMIMEInputStream : nsIInputStream * @param stream stream containing the data for the stream */ void setData(in nsIInputStream stream); + + /** + * Get the wrapped data stream + */ + readonly attribute nsIInputStream data; }; diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index d5656caae5..bf735da66f 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -1057,9 +1057,6 @@ nsIOService::SetOffline(bool offline) NS_IOSERVICE_GOING_OFFLINE_TOPIC, offlineString.get()); - if (mDNSService) - mDNSService->SetOffline(true); - if (mSocketTransportService) mSocketTransportService->SetOffline(true); @@ -1071,7 +1068,6 @@ nsIOService::SetOffline(bool offline) else if (!offline && mOffline) { // go online if (mDNSService) { - mDNSService->SetOffline(false); DebugOnly rv = mDNSService->Init(); NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed"); } diff --git a/netwerk/base/nsMIMEInputStream.cpp b/netwerk/base/nsMIMEInputStream.cpp index 7a65ac8b1b..da7c8aa661 100644 --- a/netwerk/base/nsMIMEInputStream.cpp +++ b/netwerk/base/nsMIMEInputStream.cpp @@ -159,6 +159,15 @@ nsMIMEInputStream::SetData(nsIInputStream *aStream) return NS_OK; } +NS_IMETHODIMP +nsMIMEInputStream::GetData(nsIInputStream **aStream) +{ + NS_ENSURE_ARG_POINTER(aStream); + *aStream = mData; + NS_IF_ADDREF(*aStream); + return NS_OK; +} + // set up the internal streams void nsMIMEInputStream::InitStreams() { diff --git a/netwerk/base/nsPACMan.cpp b/netwerk/base/nsPACMan.cpp index 98b3deaaca..a524fca614 100644 --- a/netwerk/base/nsPACMan.cpp +++ b/netwerk/base/nsPACMan.cpp @@ -361,8 +361,11 @@ nsPACMan::AsyncGetProxyForURI(nsIURI *uri, uint32_t appId, // Maybe Reload PAC if (!mPACURISpec.IsEmpty() && !mScheduledReload.IsNull() && - TimeStamp::Now() > mScheduledReload) + TimeStamp::Now() > mScheduledReload) { + LOG(("nsPACMan::AsyncGetProxyForURI reload as scheduled\n")); + LoadPACFromURI(EmptyCString()); + } RefPtr query = new PendingPACQuery(this, uri, appId, isInIsolatedMozBrowser, callback, @@ -404,6 +407,7 @@ nsPACMan::LoadPACFromURI(const nsCString &spec) do_CreateInstance(NS_STREAMLOADER_CONTRACTID); NS_ENSURE_STATE(loader); + LOG(("nsPACMan::LoadPACFromURI %s\n", spec.get())); // Since we might get called from nsProtocolProxyService::Init, we need to // post an event back to the main thread before we try to use the IO service. // @@ -506,6 +510,9 @@ nsPACMan::OnLoadFailure() mScheduledReload = TimeStamp::Now() + TimeDuration::FromSeconds(interval); + LOG(("OnLoadFailure: retry in %d seconds (%d fails)\n", + interval, mLoadFailureCount)); + // while we wait for the retry queued members should try direct // even if that means fast failure. PostCancelPendingQ(NS_ERROR_NOT_AVAILABLE); @@ -603,6 +610,7 @@ nsPACMan::ProcessPending() !PACURI.IsEmpty() && !PACURI.Equals(mPACURISpec)) { query->UseAlternatePACFile(PACURI); + LOG(("Use PAC from system settings: %s\n", PACURI.get())); completed = true; } @@ -613,6 +621,7 @@ nsPACMan::ProcessPending() GetProxyForURI(query->mSpec, query->mScheme, query->mHost, query->mPort, pacString))) { + LOG(("Use proxy from system settings: %s\n", pacString.get())); query->Complete(NS_OK, pacString); completed = true; } @@ -623,6 +632,7 @@ nsPACMan::ProcessPending() query->mAppId, query->mAppOrigin, query->mIsInIsolatedMozBrowser, pacString); + LOG(("Use proxy from PAC: %s\n", pacString.get())); query->Complete(status, pacString); } @@ -646,10 +656,13 @@ nsPACMan::OnStreamComplete(nsIStreamLoader *loader, // than once before the initial call completed. In this case, status // should be NS_ERROR_ABORT, and if so, then we know that we can and // should delay any processing. + LOG(("OnStreamComplete: called more than once\n")); if (status == NS_ERROR_ABORT) return NS_OK; } + LOG(("OnStreamComplete: entry\n")); + if (NS_SUCCEEDED(status) && HttpRequestSucceeded(loader)) { // Get the URI spec used to load this PAC script. nsAutoCString pacURI; @@ -681,12 +694,15 @@ nsPACMan::OnStreamComplete(nsIStreamLoader *loader, if (mPACThread) mPACThread->Dispatch(pending, nsIEventTarget::DISPATCH_NORMAL); + LOG(("OnStreamComplete: process the PAC contents\n")); + // Even if the PAC file could not be parsed, we did succeed in loading the // data for it. mLoadFailureCount = 0; } else { // We were unable to load the PAC file (presumably because of a network // failure). Try again a little later. + LOG(("OnStreamComplete: unable to load PAC, retry later\n")); OnLoadFailure(); } diff --git a/netwerk/base/nsProtocolProxyService.cpp b/netwerk/base/nsProtocolProxyService.cpp index 097130b2ab..19249361c4 100644 --- a/netwerk/base/nsProtocolProxyService.cpp +++ b/netwerk/base/nsProtocolProxyService.cpp @@ -216,12 +216,16 @@ private: void DoCallback() { + bool pacAvailable = true; if (mStatus == NS_ERROR_NOT_AVAILABLE && !mProxyInfo) { // If the PAC service is not avail (e.g. failed pac load // or shutdown) then we will be going direct. Make that // mapping now so that any filters are still applied. mPACString = NS_LITERAL_CSTRING("DIRECT;"); mStatus = NS_OK; + + LOG(("pac not available, use DIRECT\n")); + pacAvailable = false; } // Generate proxy info from the PAC string if appropriate @@ -239,7 +243,10 @@ private: else mProxyInfo = nullptr; - LOG(("pac thread callback %s\n", mPACString.get())); + if(pacAvailable) { + // if !pacAvailable, it was already logged above + LOG(("pac thread callback %s\n", mPACString.get())); + } if (NS_SUCCEEDED(mStatus)) mPPS->MaybeDisableDNSPrefetch(mProxyInfo); mCallback->OnProxyAvailable(this, mChannel, mProxyInfo, mStatus); diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp index b1df84b615..71e89f6151 100644 --- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -1420,6 +1420,20 @@ nsSocketTransport::InitiateSocket() NetAddrToPRNetAddr(&mNetAddr, &prAddr); +#ifdef XP_WIN + // Find the real tcp socket and set non-blocking once again! + // Bug 1158189. + PRFileDesc *bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER); + if (bottom) { + PROsfd osfd = PR_FileDesc2NativeHandle(bottom); + u_long nonblocking = 1; + if (ioctlsocket(osfd, FIONBIO, &nonblocking) != 0) { + NS_WARNING("Socket could not be set non-blocking!"); + return NS_ERROR_FAILURE; + } + } +#endif + // We use PRIntervalTime here because we need // nsIOService::LastOfflineStateChange time and // nsIOService::LastConectivityChange time to be atomic. @@ -2972,10 +2986,10 @@ nsSocketTransport::PRFileDescAutoLock::SetKeepaliveVals(bool aEnabled, #if defined(XP_WIN) // Windows allows idle time and retry interval to be set; NOT ping count. struct tcp_keepalive keepalive_vals = { - (int)aEnabled, + (u_long)aEnabled, // Windows uses msec. - aIdleTime * 1000, - aRetryInterval * 1000 + (u_long)(aIdleTime * 1000UL), + (u_long)(aRetryInterval * 1000UL) }; DWORD bytes_returned; int err = WSAIoctl(sock, SIO_KEEPALIVE_VALS, &keepalive_vals, diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index 74232200c1..f7aaf2dc60 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -819,26 +819,29 @@ nsSocketTransportService::Run() gSocketThread = PR_GetCurrentThread(); - mPollableEvent.reset(new PollableEvent()); - // - // NOTE: per bug 190000, this failure could be caused by Zone-Alarm - // or similar software. - // - // NOTE: per bug 191739, this failure could also be caused by lack - // of a loopback device on Windows and OS/2 platforms (it creates - // a loopback socket pair on these platforms to implement a pollable - // event object). if we can't create a pollable event, then we'll - // have to "busy wait" to implement the socket event queue :-( - // - if (!mPollableEvent->Valid()) { - mPollableEvent = nullptr; - NS_WARNING("running socket transport thread without a pollable event"); - SOCKET_LOG(("running socket transport thread without a pollable event")); - } + { + DebugMutexAutoLock lock(mLock); + mPollableEvent.reset(new PollableEvent()); + // + // NOTE: per bug 190000, this failure could be caused by Zone-Alarm + // or similar software. + // + // NOTE: per bug 191739, this failure could also be caused by lack + // of a loopback device on Windows and OS/2 platforms (it creates + // a loopback socket pair on these platforms to implement a pollable + // event object). if we can't create a pollable event, then we'll + // have to "busy wait" to implement the socket event queue :-( + // + if (!mPollableEvent->Valid()) { + mPollableEvent = nullptr; + NS_WARNING("running socket transport thread without a pollable event"); + SOCKET_LOG(("running socket transport thread without a pollable event")); + } - mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; - mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; - mPollList[0].out_flags = 0; + mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr; + mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; + mPollList[0].out_flags = 0; + } mRawThread = NS_GetCurrentThread(); diff --git a/netwerk/base/nsStandardURL.cpp b/netwerk/base/nsStandardURL.cpp index fba326ca0d..c1216f2d57 100644 --- a/netwerk/base/nsStandardURL.cpp +++ b/netwerk/base/nsStandardURL.cpp @@ -1261,6 +1261,29 @@ nsStandardURL::GetOriginCharset(nsACString &result) return NS_OK; } +static bool +IsSpecialProtocol(const nsACString &input) +{ + nsACString::const_iterator start, end; + input.BeginReading(start); + nsACString::const_iterator iterator(start); + input.EndReading(end); + + while (iterator != end && *iterator != ':') { + iterator++; + } + + nsAutoCString protocol(nsDependentCSubstring(start.get(), iterator.get())); + + return protocol.LowerCaseEqualsLiteral("http") || + protocol.LowerCaseEqualsLiteral("https") || + protocol.LowerCaseEqualsLiteral("ftp") || + protocol.LowerCaseEqualsLiteral("ws") || + protocol.LowerCaseEqualsLiteral("wss") || + protocol.LowerCaseEqualsLiteral("file") || + protocol.LowerCaseEqualsLiteral("gopher"); +} + NS_IMETHODIMP nsStandardURL::SetSpec(const nsACString &input) { @@ -1291,12 +1314,35 @@ nsStandardURL::SetSpec(const nsACString &input) Clear(); // filter out unexpected chars "\r\n\t" if necessary - nsAutoCString buf1; - if (net_FilterURIString(spec, buf1)) { - spec = buf1.get(); - specLength = buf1.Length(); + nsAutoCString filteredURI; + if (!net_FilterURIString(spec, filteredURI)) { + // Copy the content into filteredURI even if no whitespace was stripped. + // We need a non-const buffer to perform backslash replacement. + filteredURI = input; } + if (IsSpecialProtocol(filteredURI)) { + // Bug 652186: Replace all backslashes with slashes when parsing paths + // Stop when we reach the query or the hash. + nsAutoCString::iterator start; + nsAutoCString::iterator end; + filteredURI.BeginWriting(start); + filteredURI.EndWriting(end); + while (start != end) { + if (*start == '?' || *start == '#') { + break; + } + if (*start == '\\') { + *start = '/'; + } + start++; + } + } + + spec = filteredURI.get(); + specLength = filteredURI.Length(); + + // parse the given URL... nsresult rv = ParseURL(spec, specLength); if (NS_SUCCEEDED(rv)) { @@ -2078,12 +2124,15 @@ nsStandardURL::Resolve(const nsACString &in, nsACString &out) // filter out unexpected chars "\r\n\t" if necessary nsAutoCString buf; int32_t relpathLen; - if (net_FilterURIString(relpath, buf)) { - relpath = buf.get(); - relpathLen = buf.Length(); - } else - relpathLen = flat.Length(); - + if (!net_FilterURIString(relpath, buf)) { + // Copy the content into filteredURI even if no whitespace was stripped. + // We need a non-const buffer to perform backslash replacement. + buf = in; + } + + relpath = buf.get(); + relpathLen = buf.Length(); + char *result = nullptr; LOG(("nsStandardURL::Resolve [this=%p spec=%s relpath=%s]\n", @@ -2120,6 +2169,30 @@ nsStandardURL::Resolve(const nsACString &in, nsACString &out) // reset the scheme and assume a relative url if (NS_FAILED(rv)) scheme.Reset(); + nsAutoCString protocol(Segment(scheme)); + nsAutoCString baseProtocol(Scheme()); + + // We need to do backslash replacement for the following cases: + // 1. The input is an absolute path with a http/https/ftp scheme + // 2. The input is a relative path, and the base URL has a http/https/ftp scheme + if ((protocol.IsEmpty() && IsSpecialProtocol(baseProtocol)) || + IsSpecialProtocol(protocol)) { + + nsAutoCString::iterator start; + nsAutoCString::iterator end; + buf.BeginWriting(start); + buf.EndWriting(end); + while (start != end) { + if (*start == '?' || *start == '#') { + break; + } + if (*start == '\\') { + *start = '/'; + } + start++; + } + } + if (scheme.mLen >= 0) { // add some flags to coalesceFlag if it is an ftp-url // need this later on when coalescing the resulting URL diff --git a/netwerk/base/nsURLHelper.cpp b/netwerk/base/nsURLHelper.cpp index e6c43842f3..e2fffeb28d 100644 --- a/netwerk/base/nsURLHelper.cpp +++ b/netwerk/base/nsURLHelper.cpp @@ -236,7 +236,7 @@ net_CoalesceDirs(netCoalesceFlags flags, char* path) */ char *fwdPtr = path; char *urlPtr = path; - char *lastslash = path; + char *endPath = path; uint32_t traversal = 0; uint32_t special_ftp_len = 0; @@ -253,34 +253,18 @@ net_CoalesceDirs(netCoalesceFlags flags, char* path) special_ftp_len = 2; } - /* find the last slash before # or ? */ - for(; (*fwdPtr != '\0') && - (*fwdPtr != '?') && + /* find the end of the path - places the cursor on \0, ? or # */ + for(; (*fwdPtr != '\0') && + (*fwdPtr != '?') && (*fwdPtr != '#'); ++fwdPtr) { } - /* found nothing, but go back one only */ - /* if there is something to go back to */ - if (fwdPtr != path && *fwdPtr == '\0') - { - --fwdPtr; - } - - /* search the slash */ - for(; (fwdPtr != path) && - (*fwdPtr != '/'); --fwdPtr) - { - } - lastslash = fwdPtr; + endPath = fwdPtr; fwdPtr = path; /* replace all %2E or %2e with . in the path */ - /* but stop at lastchar if non null */ - for(; (*fwdPtr != '\0') && - (*fwdPtr != '?') && - (*fwdPtr != '#') && - (*lastslash == '\0' || fwdPtr != lastslash); ++fwdPtr) + for(; fwdPtr != endPath; ++fwdPtr) { if (*fwdPtr == '%' && *(fwdPtr+1) == '2' && (*(fwdPtr+2) == 'E' || *(fwdPtr+2) == 'e')) diff --git a/netwerk/base/nsURLParsers.cpp b/netwerk/base/nsURLParsers.cpp index a024174f04..ac22ae7540 100644 --- a/netwerk/base/nsURLParsers.cpp +++ b/netwerk/base/nsURLParsers.cpp @@ -62,17 +62,21 @@ nsBaseURLParser::ParseURL(const char *spec, int32_t specLen, const char *stop = nullptr; const char *colon = nullptr; const char *slash = nullptr; - const char *p; + const char *p = spec; uint32_t offset = 0; int32_t len = specLen; - for (p = spec; len && *p && !colon && !slash; ++p, --len) { - // skip leading whitespace - if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') { - spec++; - specLen--; - offset++; - continue; - } + + // skip leading whitespace + while (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') { + spec++; + specLen--; + offset++; + + p++; + len--; + } + + for (; len && *p && !colon && !slash; ++p, --len) { switch (*p) { case ':': if (!colon) diff --git a/netwerk/cache/nsDiskCacheDeviceSQL.cpp b/netwerk/cache/nsDiskCacheDeviceSQL.cpp index 512ef984bb..c649dc8aff 100644 --- a/netwerk/cache/nsDiskCacheDeviceSQL.cpp +++ b/netwerk/cache/nsDiskCacheDeviceSQL.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; indent-tab-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cin: */ /* 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 diff --git a/netwerk/cache2/CacheEntry.cpp b/netwerk/cache2/CacheEntry.cpp index 0b041db17e..92179e200d 100644 --- a/netwerk/cache2/CacheEntry.cpp +++ b/netwerk/cache2/CacheEntry.cpp @@ -908,56 +908,61 @@ void CacheEntry::OnHandleClosed(CacheEntryHandle const* aHandle) { LOG(("CacheEntry::OnHandleClosed [this=%p, state=%s, handle=%p]", this, StateString(mState), aHandle)); - nsCOMPtr outputStream; + mozilla::MutexAutoLock lock(mLock); - { - mozilla::MutexAutoLock lock(mLock); - - if (mWriter != aHandle) { - LOG((" not the writer")); - return; - } - - if (mOutputStream) { - // No one took our internal output stream, so there are no data - // and output stream has to be open symultaneously with input stream - // on this entry again. - mHasData = false; - } - - outputStream.swap(mOutputStream); - mWriter = nullptr; - - if (mState == WRITING) { - LOG((" reverting to state EMPTY - write failed")); - mState = EMPTY; - } - else if (mState == REVALIDATING) { - LOG((" reverting to state READY - reval failed")); - mState = READY; - } - - if (mState == READY && !mHasData) { - // We may get to this state when following steps happen: - // 1. a new entry is given to a consumer - // 2. the consumer calls MetaDataReady(), we transit to READY - // 3. abandons the entry w/o opening the output stream, mHasData left false - // - // In this case any following consumer will get a ready entry (with metadata) - // but in state like the entry data write was still happening (was in progress) - // and will indefinitely wait for the entry data or even the entry itself when - // RECHECK_AFTER_WRITE is returned from onCacheEntryCheck. - LOG((" we are in READY state, pretend we have data regardless it" - " has actully been never touched")); - mHasData = true; - } - - InvokeCallbacks(); + if (IsDoomed() && mHandlesCount == 0 && NS_SUCCEEDED(mFileStatus)) { + // This entry is no longer referenced from outside and is doomed. + // Tell the file to kill the handle, i.e. bypass any I/O operations + // on it except removing the file. + mFile->Kill(); } - if (outputStream) { + if (mWriter != aHandle) { + LOG((" not the writer")); + return; + } + + if (mOutputStream) { LOG((" abandoning phantom output stream")); - outputStream->Close(); + // No one took our internal output stream, so there are no data + // and output stream has to be open symultaneously with input stream + // on this entry again. + mHasData = false; + // This asynchronously ends up invoking callbacks on this entry + // through OnOutputClosed() call. + mOutputStream->Close(); + mOutputStream = nullptr; + } else { + // We must always redispatch, otherwise there is a risk of stack + // overflow. This code can recurse deeply. It won't execute sooner + // than we release mLock. + BackgroundOp(Ops::CALLBACKS, true); + } + + mWriter = nullptr; + + if (mState == WRITING) { + LOG((" reverting to state EMPTY - write failed")); + mState = EMPTY; + } + else if (mState == REVALIDATING) { + LOG((" reverting to state READY - reval failed")); + mState = READY; + } + + if (mState == READY && !mHasData) { + // We may get to this state when following steps happen: + // 1. a new entry is given to a consumer + // 2. the consumer calls MetaDataReady(), we transit to READY + // 3. abandons the entry w/o opening the output stream, mHasData left false + // + // In this case any following consumer will get a ready entry (with metadata) + // but in state like the entry data write was still happening (was in progress) + // and will indefinitely wait for the entry data or even the entry itself when + // RECHECK_AFTER_WRITE is returned from onCacheEntryCheck. + LOG((" we are in READY state, pretend we have data regardless it" + " has actully been never touched")); + mHasData = true; } } @@ -1101,8 +1106,10 @@ NS_IMETHODIMP CacheEntry::OpenInputStream(int64_t offset, nsIInputStream * *_ret nsresult rv; + RefPtr selfHandle = NewHandle(); + nsCOMPtr stream; - rv = mFile->OpenInputStream(getter_AddRefs(stream)); + rv = mFile->OpenInputStream(selfHandle, getter_AddRefs(stream)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr seekable = diff --git a/netwerk/cache2/CacheFile.cpp b/netwerk/cache2/CacheFile.cpp index c42b57f1f5..75eb1ab6e3 100644 --- a/netwerk/cache2/CacheFile.cpp +++ b/netwerk/cache2/CacheFile.cpp @@ -193,6 +193,7 @@ CacheFile::CacheFile() , mPreloadChunkCount(0) , mStatus(NS_OK) , mDataSize(-1) + , mKill(false) , mOutput(nullptr) { LOG(("CacheFile::CacheFile() [this=%p]", this)); @@ -203,7 +204,7 @@ CacheFile::~CacheFile() LOG(("CacheFile::~CacheFile() [this=%p]", this)); MutexAutoLock lock(mLock); - if (!mMemoryOnly && mReady) { + if (!mMemoryOnly && mReady && !mKill) { // mReady flag indicates we have metadata plus in a valid state. WriteMetadataIfNeededLocked(true); } @@ -702,8 +703,18 @@ CacheFile::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) return NS_ERROR_UNEXPECTED; } +bool CacheFile::IsKilled() +{ + bool killed = mKill; + if (killed) { + LOG(("CacheFile is killed, this=%p", this)); + } + + return killed; +} + nsresult -CacheFile::OpenInputStream(nsIInputStream **_retval) +CacheFile::OpenInputStream(nsICacheEntry *aEntryHandle, nsIInputStream **_retval) { CacheFileAutoLock lock(this); @@ -734,7 +745,7 @@ CacheFile::OpenInputStream(nsIInputStream **_retval) // the last input stream is closed. mPreloadWithoutInputStreams = false; - CacheFileInputStream *input = new CacheFileInputStream(this); + CacheFileInputStream *input = new CacheFileInputStream(this, aEntryHandle); LOG(("CacheFile::OpenInputStream() - Creating new input stream %p [this=%p]", input, this)); @@ -906,6 +917,9 @@ nsresult CacheFile::SetElement(const char *aKey, const char *aValue) { CacheFileAutoLock lock(this); + + LOG(("CacheFile::SetElement() this=%p", this)); + MOZ_ASSERT(mMetadata); NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED); @@ -940,6 +954,10 @@ nsresult CacheFile::SetExpirationTime(uint32_t aExpirationTime) { CacheFileAutoLock lock(this); + + LOG(("CacheFile::SetExpirationTime() this=%p, expiration=%u", + this, aExpirationTime)); + MOZ_ASSERT(mMetadata); NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED); @@ -965,6 +983,10 @@ nsresult CacheFile::SetFrecency(uint32_t aFrecency) { CacheFileAutoLock lock(this); + + LOG(("CacheFile::SetFrecency() this=%p, frecency=%u", + this, aFrecency)); + MOZ_ASSERT(mMetadata); NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED); @@ -1020,6 +1042,9 @@ nsresult CacheFile::OnFetched() { CacheFileAutoLock lock(this); + + LOG(("CacheFile::OnFetched() this=%p", this)); + MOZ_ASSERT(mMetadata); NS_ENSURE_TRUE(mMetadata, NS_ERROR_UNEXPECTED); diff --git a/netwerk/cache2/CacheFile.h b/netwerk/cache2/CacheFile.h index d6de9401a0..54b61dd52f 100644 --- a/netwerk/cache2/CacheFile.h +++ b/netwerk/cache2/CacheFile.h @@ -74,15 +74,17 @@ public: NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override; NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override; NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override; + virtual bool IsKilled() override; NS_IMETHOD OnMetadataRead(nsresult aResult) override; NS_IMETHOD OnMetadataWritten(nsresult aResult) override; - NS_IMETHOD OpenInputStream(nsIInputStream **_retval); + NS_IMETHOD OpenInputStream(nsICacheEntry *aCacheEntryHandle, nsIInputStream **_retval); NS_IMETHOD OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval); NS_IMETHOD SetMemoryOnly(); NS_IMETHOD Doom(CacheFileListener *aCallback); + void Kill() { mKill = true; } nsresult ThrowMemoryCachedData(); // metadata forwarders @@ -198,6 +200,7 @@ private: RefPtr mMetadata; nsCOMPtr mListener; nsCOMPtr mDoomAfterOpenListener; + Atomic mKill; nsRefPtrHashtable mChunks; nsClassHashtable mChunkListeners; diff --git a/netwerk/cache2/CacheFileChunk.cpp b/netwerk/cache2/CacheFileChunk.cpp index bef03b153d..97987f645d 100644 --- a/netwerk/cache2/CacheFileChunk.cpp +++ b/netwerk/cache2/CacheFileChunk.cpp @@ -594,6 +594,12 @@ CacheFileChunk::OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) return NS_ERROR_UNEXPECTED; } +bool +CacheFileChunk::IsKilled() +{ + return mFile->IsKilled(); +} + bool CacheFileChunk::IsReady() const { diff --git a/netwerk/cache2/CacheFileChunk.h b/netwerk/cache2/CacheFileChunk.h index 55ee9d3b55..b8ad92dad6 100644 --- a/netwerk/cache2/CacheFileChunk.h +++ b/netwerk/cache2/CacheFileChunk.h @@ -94,6 +94,7 @@ public: NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override; NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override; NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override; + virtual bool IsKilled() override; bool IsReady() const; bool IsDirty() const; diff --git a/netwerk/cache2/CacheFileIOManager.cpp b/netwerk/cache2/CacheFileIOManager.cpp index dafa653ca5..60e5a60013 100644 --- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -662,7 +662,7 @@ public: { nsresult rv; - if (mHandle->IsClosed()) { + if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) { rv = NS_ERROR_NOT_INITIALIZED; } else { rv = CacheFileIOManager::gInstance->ReadInternal( @@ -712,7 +712,7 @@ public: { nsresult rv; - if (mHandle->IsClosed()) { + if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) { // We usually get here only after the internal shutdown // (i.e. mShuttingDown == true). Pretend write has succeeded // to avoid any past-shutdown file dooming. @@ -885,7 +885,7 @@ public: { nsresult rv; - if (mHandle->IsClosed()) { + if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) { rv = NS_ERROR_NOT_INITIALIZED; } else { rv = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal( @@ -1913,7 +1913,7 @@ CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset, nsresult rv; RefPtr ioMan = gInstance; - if (aHandle->IsClosed() || !ioMan) { + if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) { if (!aCallback) { // When no callback is provided, CacheFileIOManager is responsible for // releasing the buffer. We must release it even in case of failure. @@ -2317,7 +2317,7 @@ CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle, nsresult rv; RefPtr ioMan = gInstance; - if (aHandle->IsClosed() || !ioMan) { + if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) { return NS_ERROR_NOT_INITIALIZED; } @@ -3750,6 +3750,8 @@ CacheFileIOManager::CreateCacheTree() nsresult CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate) { + LOG(("CacheFileIOManager::OpenNSPRHandle BEGIN, handle=%p", aHandle)); + MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased()); MOZ_ASSERT(!aHandle->mFD); MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex); @@ -3807,6 +3809,9 @@ CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate) } mHandlesByLastUsed.AppendElement(aHandle); + + LOG(("CacheFileIOManager::OpenNSPRHandle END, handle=%p", aHandle)); + return NS_OK; } diff --git a/netwerk/cache2/CacheFileIOManager.h b/netwerk/cache2/CacheFileIOManager.h index 8f0f6d34d5..7a2c9eca15 100644 --- a/netwerk/cache2/CacheFileIOManager.h +++ b/netwerk/cache2/CacheFileIOManager.h @@ -234,6 +234,8 @@ public: NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) = 0; NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) = 0; NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) = 0; + + virtual bool IsKilled() { return false; } }; NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileIOListener, CACHEFILEIOLISTENER_IID) diff --git a/netwerk/cache2/CacheFileInputStream.cpp b/netwerk/cache2/CacheFileInputStream.cpp index 108a681da6..5d6e976237 100644 --- a/netwerk/cache2/CacheFileInputStream.cpp +++ b/netwerk/cache2/CacheFileInputStream.cpp @@ -42,7 +42,7 @@ NS_INTERFACE_MAP_BEGIN(CacheFileInputStream) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) NS_INTERFACE_MAP_END_THREADSAFE -CacheFileInputStream::CacheFileInputStream(CacheFile *aFile) +CacheFileInputStream::CacheFileInputStream(CacheFile *aFile, nsISupports *aEntry) : mFile(aFile) , mPos(0) , mClosed(false) @@ -50,6 +50,7 @@ CacheFileInputStream::CacheFileInputStream(CacheFile *aFile) , mWaitingForUpdate(false) , mListeningForChunk(-1) , mCallbackFlags(0) + , mCacheEntryHandle(aEntry) { LOG(("CacheFileInputStream::CacheFileInputStream() [this=%p]", this)); MOZ_COUNT_CTOR(CacheFileInputStream); @@ -240,6 +241,8 @@ CacheFileInputStream::CloseWithStatusLocked(nsresult aStatus) MaybeNotifyListener(); + mFile->ReleaseOutsideLock(mCacheEntryHandle.forget()); + return NS_OK; } diff --git a/netwerk/cache2/CacheFileInputStream.h b/netwerk/cache2/CacheFileInputStream.h index 2b75420eed..f61ad8d189 100644 --- a/netwerk/cache2/CacheFileInputStream.h +++ b/netwerk/cache2/CacheFileInputStream.h @@ -11,7 +11,6 @@ #include "nsAutoPtr.h" #include "CacheFileChunk.h" - namespace mozilla { namespace net { @@ -27,7 +26,7 @@ class CacheFileInputStream : public nsIAsyncInputStream NS_DECL_NSISEEKABLESTREAM public: - explicit CacheFileInputStream(CacheFile *aFile); + explicit CacheFileInputStream(CacheFile *aFile, nsISupports *aEntry); NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override; NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) override; @@ -64,6 +63,8 @@ private: nsCOMPtr mCallback; uint32_t mCallbackFlags; nsCOMPtr mCallbackTarget; + // Held purely for referencing purposes + RefPtr mCacheEntryHandle; }; diff --git a/netwerk/cache2/CacheFileMetadata.h b/netwerk/cache2/CacheFileMetadata.h index 8bfe5ca02f..0bc7f94820 100644 --- a/netwerk/cache2/CacheFileMetadata.h +++ b/netwerk/cache2/CacheFileMetadata.h @@ -116,6 +116,7 @@ public: NS_IMETHOD OnMetadataRead(nsresult aResult) = 0; NS_IMETHOD OnMetadataWritten(nsresult aResult) = 0; + virtual bool IsKilled() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(CacheFileMetadataListener, @@ -183,6 +184,7 @@ public: NS_IMETHOD OnFileDoomed(CacheFileHandle *aHandle, nsresult aResult) override; NS_IMETHOD OnEOFSet(CacheFileHandle *aHandle, nsresult aResult) override; NS_IMETHOD OnFileRenamed(CacheFileHandle *aHandle, nsresult aResult) override; + virtual bool IsKilled() override { return mListener && mListener->IsKilled(); } // Memory reporting size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp index d202c68fce..a7b49679e3 100644 --- a/netwerk/dns/ChildDNSService.cpp +++ b/netwerk/dns/ChildDNSService.cpp @@ -4,11 +4,13 @@ #include "mozilla/net/ChildDNSService.h" #include "nsIDNSListener.h" +#include "nsIIOService.h" #include "nsIThread.h" #include "nsThreadUtils.h" #include "nsIXPConnect.h" #include "nsIPrefService.h" #include "nsIProtocolProxyService.h" +#include "nsNetCID.h" #include "mozilla/net/NeckoChild.h" #include "mozilla/net/DNSListenerProxy.h" #include "nsServiceManagerUtils.h" @@ -42,7 +44,6 @@ NS_IMPL_ISUPPORTS(ChildDNSService, ChildDNSService::ChildDNSService() : mFirstTime(true) - , mOffline(false) , mDisablePrefetch(false) , mPendingRequestsLock("DNSPendingRequestsLock") { @@ -103,7 +104,7 @@ ChildDNSService::AsyncResolveExtended(const nsACString &hostname, // Support apps being 'offline' even if parent is not: avoids DNS traffic by // apps that have been told they are offline. - if (mOffline) { + if (GetOffline()) { flags |= RESOLVE_OFFLINE; } @@ -298,18 +299,15 @@ ChildDNSService::SetPrefetchEnabled(bool inVal) return NS_OK; } -NS_IMETHODIMP -ChildDNSService::GetOffline(bool* aResult) +bool +ChildDNSService::GetOffline() const { - *aResult = mOffline; - return NS_OK; -} - -NS_IMETHODIMP -ChildDNSService::SetOffline(bool value) -{ - mOffline = value; - return NS_OK; + bool offline = false; + nsCOMPtr io = do_GetService(NS_IOSERVICE_CONTRACTID); + if (io) { + io->GetOffline(&offline); + } + return offline; } //----------------------------------------------------------------------------- diff --git a/netwerk/dns/ChildDNSService.h b/netwerk/dns/ChildDNSService.h index b2c3a1dbcc..1662960bbe 100644 --- a/netwerk/dns/ChildDNSService.h +++ b/netwerk/dns/ChildDNSService.h @@ -35,6 +35,8 @@ public: static ChildDNSService* GetSingleton(); void NotifyRequestDone(DNSRequestChild *aDnsRequest); + + bool GetOffline() const; private: virtual ~ChildDNSService(); @@ -45,7 +47,6 @@ private: nsACString &aHashKey); bool mFirstTime; - bool mOffline; bool mDisablePrefetch; // We need to remember pending dns requests to be able to cancel them. diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 0a393128eb..37f4b35676 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -481,7 +481,6 @@ nsDNSService::nsDNSService() , mDisableIPv6(false) , mDisablePrefetch(false) , mFirstTime(true) - , mOffline(false) , mNotifyResolution(false) , mOfflineLocalhost(false) { @@ -669,18 +668,15 @@ nsDNSService::Shutdown() return NS_OK; } -NS_IMETHODIMP -nsDNSService::GetOffline(bool *offline) +bool +nsDNSService::GetOffline() const { - *offline = mOffline; - return NS_OK; -} - -NS_IMETHODIMP -nsDNSService::SetOffline(bool offline) -{ - mOffline = offline; - return NS_OK; + bool offline = false; + nsCOMPtr io = do_GetService(NS_IOSERVICE_CONTRACTID); + if (io) { + io->GetOffline(&offline); + } + return offline; } NS_IMETHODIMP @@ -776,7 +772,7 @@ nsDNSService::AsyncResolveExtended(const nsACString &aHostname, return rv; } - if (mOffline && + if (GetOffline() && (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { flags |= RESOLVE_OFFLINE; } @@ -891,7 +887,7 @@ nsDNSService::Resolve(const nsACString &aHostname, return rv; } - if (mOffline && + if (GetOffline() && (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) { flags |= RESOLVE_OFFLINE; } diff --git a/netwerk/dns/nsDNSService2.h b/netwerk/dns/nsDNSService2.h index a33e2dd9b4..79454b901b 100644 --- a/netwerk/dns/nsDNSService2.h +++ b/netwerk/dns/nsDNSService2.h @@ -36,6 +36,8 @@ public: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + bool GetOffline() const; + private: ~nsDNSService(); @@ -62,7 +64,6 @@ private: bool mDisablePrefetch; bool mBlockDotOnion; bool mFirstTime; - bool mOffline; bool mNotifyResolution; bool mOfflineLocalhost; nsTHashtable mLocalDomains; diff --git a/netwerk/dns/nsPIDNSService.idl b/netwerk/dns/nsPIDNSService.idl index d89f1bdfb8..9bed0555c5 100644 --- a/netwerk/dns/nsPIDNSService.idl +++ b/netwerk/dns/nsPIDNSService.idl @@ -10,7 +10,7 @@ * This is a private interface used by the internals of the networking library. * It will never be frozen. Do not use it in external code. */ -[scriptable, uuid(6b16fb1f-5709-4c94-a89f-a22be873c54d)] +[scriptable, uuid(24e598fd-7b1a-436c-9154-14d8b38df8a5)] interface nsPIDNSService : nsIDNSService { /** @@ -31,9 +31,4 @@ interface nsPIDNSService : nsIDNSService * Whether or not DNS prefetching (aka RESOLVE_SPECULATE) is enabled */ attribute boolean prefetchEnabled; - - /** - * @return whether the DNS service is offline. - */ - attribute boolean offline; }; diff --git a/netwerk/test/unit/test_standardurl.js b/netwerk/test/unit/test_standardurl.js index 9d186efad5..92de451d63 100644 --- a/netwerk/test/unit/test_standardurl.js +++ b/netwerk/test/unit/test_standardurl.js @@ -301,8 +301,47 @@ add_test(function test_hugeStringThrows() run_next_test(); }); +add_test(function test_pathPercentEncodedDot() +{ + var url = stringToURL("http://example.com/%2eX/X%2e/%2eX"); + do_check_eq(url.spec, "http://example.com/.X/X./.X"); + + url = stringToURL("http://example.com/hello/%2e%2E/%2e"); + do_check_eq(url.spec, "http://example.com/"); + + url = stringToURL("http://example.com/hello/%2e%2E/%"); + do_check_eq(url.spec, "http://example.com/%"); + + url = stringToURL("http://example.com/hello/%2e%2E/%2"); + do_check_eq(url.spec, "http://example.com/%2"); + + url = stringToURL("http://example.com/hello/%2e%2E/%#"); + do_check_eq(url.spec, "http://example.com/%#"); + + url = stringToURL("http://example.com/hello/%2e%2E/%2?"); + do_check_eq(url.spec, "http://example.com/%2?"); + + url = stringToURL("http://example.com/hello/%2e/"); + do_check_eq(url.spec, "http://example.com/hello/"); + + run_next_test(); +}); + add_test(function test_filterWhitespace() { var url = stringToURL(" \r\n\th\nt\rt\tp://ex\r\n\tample.com/path\r\n\t/\r\n\tto the/fil\r\n\te.e\r\n\txt?que\r\n\try#ha\r\n\tsh \r\n\t "); do_check_eq(url.spec, "http://example.com/path/to%20the/file.ext?query#hash"); }); + +add_test(function test_backslashReplacement() +{ + var url = stringToURL("http:\\\\test.com\\path/to\\file?query\\backslash#hash\\"); + do_check_eq(url.spec, "http://test.com/path/to/file?query\\backslash#hash\\"); + + url = stringToURL("http:\\\\test.com\\example.org/path\\to/file"); + do_check_eq(url.spec, "http://test.com/example.org/path/to/file"); + do_check_eq(url.host, "test.com"); + do_check_eq(url.path, "/example.org/path/to/file"); + + run_next_test(); +}); diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index 6a23d96ef4..140b6576ce 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -14,7 +14,6 @@ user_pref("dom.forms.color", true); // on for testing user_pref("dom.max_script_run_time", 0); // no slow script dialogs user_pref("hangmonitor.timeout", 0); // no hang monitor user_pref("dom.max_chrome_script_run_time", 0); -user_pref("dom.max_child_script_run_time", 0); user_pref("dom.ipc.reportProcessHangs", false); // process hang monitor user_pref("dom.popup_maximum", -1); user_pref("dom.send_after_paint_to_content", true); diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 1eff99df79..1f6d359b59 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -18900,57 +18900,86 @@ "path": "web-animations/animation-effect-timing/getComputedStyle.html", "url": "/web-animations/animation-effect-timing/getComputedStyle.html" }, - { - "path": "web-animations/animation-effect-timing/iterations.html", - "url": "/web-animations/animation-effect-timing/iterations.html" - }, { "path": "web-animations/animation-effect-timing/iterationStart.html", "url": "/web-animations/animation-effect-timing/iterationStart.html" }, { - "path": "web-animations/animation-node/animation-node-after.html", - "url": "/web-animations/animation-node/animation-node-after.html" + "path": "web-animations/animation-effect-timing/iterations.html", + "url": "/web-animations/animation-effect-timing/iterations.html" }, { - "path": "web-animations/animation-node/animation-node-before.html", - "url": "/web-animations/animation-node/animation-node-before.html" + "path": "web-animations/animation-model/animation-types/discrete-animation.html", + "url": "/web-animations/animation-model/animation-types/discrete-animation.html" }, { - "path": "web-animations/animation-node/animation-node-next-sibling.html", - "url": "/web-animations/animation-node/animation-node-next-sibling.html" + "path": "web-animations/animation-model/animation-types/not-animatable.html", + "url": "/web-animations/animation-model/animation-types/not-animatable.html" }, { - "path": "web-animations/animation-node/animation-node-parent.html", - "url": "/web-animations/animation-node/animation-node-parent.html" + "path": "web-animations/animation-model/keyframes/effect-value-context.html", + "url": "/web-animations/animation-model/keyframes/effect-value-context.html" }, { - "path": "web-animations/animation-node/animation-node-previous-sibling.html", - "url": "/web-animations/animation-node/animation-node-previous-sibling.html" - }, - { - "path": "web-animations/animation-node/animation-node-remove.html", - "url": "/web-animations/animation-node/animation-node-remove.html" - }, - { - "path": "web-animations/animation-node/animation-node-replace.html", - "url": "/web-animations/animation-node/animation-node-replace.html" - }, - { - "path": "web-animations/animation-node/idlharness.html", - "url": "/web-animations/animation-node/idlharness.html" - }, - { - "url": "/web-animations/animation-timeline/animation-timeline.html" + "path": "web-animations/animation-timeline/document-timeline.html", + "url": "/web-animations/animation-timeline/document-timeline.html" }, { "path": "web-animations/animation-timeline/idlharness.html", "url": "/web-animations/animation-timeline/idlharness.html" }, + { + "path": "web-animations/animation/cancel.html", + "url": "/web-animations/animation/cancel.html" + }, { "path": "web-animations/animation/constructor.html", "url": "/web-animations/animation/constructor.html" }, + { + "path": "web-animations/animation/finish.html", + "url": "/web-animations/animation/finish.html" + }, + { + "path": "web-animations/animation/finished.html", + "url": "/web-animations/animation/finished.html" + }, + { + "path": "web-animations/animation/id.html", + "url": "/web-animations/animation/id.html" + }, + { + "path": "web-animations/animation/oncancel.html", + "url": "/web-animations/animation/oncancel.html" + }, + { + "path": "web-animations/animation/onfinish.html", + "url": "/web-animations/animation/onfinish.html" + }, + { + "path": "web-animations/animation/pause.html", + "url": "/web-animations/animation/pause.html" + }, + { + "path": "web-animations/animation/play.html", + "url": "/web-animations/animation/play.html" + }, + { + "path": "web-animations/animation/playState.html", + "url": "/web-animations/animation/playState.html" + }, + { + "path": "web-animations/animation/playbackRate.html", + "url": "/web-animations/animation/playbackRate.html" + }, + { + "path": "web-animations/animation/ready.html", + "url": "/web-animations/animation/ready.html" + }, + { + "path": "web-animations/animation/reverse.html", + "url": "/web-animations/animation/reverse.html" + }, { "path": "web-animations/keyframe-effect/constructor.html", "url": "/web-animations/keyframe-effect/constructor.html" diff --git a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute.html.ini b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute.html.ini index 865150c6d8..57d9b9f411 100644 --- a/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute.html.ini +++ b/testing/web-platform/meta/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute.html.ini @@ -168,18 +168,12 @@ [ ref sizes="1px" (standards mode)] expected: FAIL - [ ref sizes="100vw" (standards mode)] - expected: FAIL - [ ref sizes="100vw" (standards mode)] expected: FAIL [ ref sizes="100vw" (standards mode)] expected: FAIL - [ ref sizes="100vw" (standards mode)] - expected: FAIL - [ ref sizes="100vw" (standards mode)] expected: FAIL @@ -354,18 +348,12 @@ [ ref sizes="1px" (quirks mode)] expected: FAIL - [ ref sizes="100vw" (quirks mode)] - expected: FAIL - [ ref sizes="100vw" (quirks mode)] expected: FAIL [ ref sizes="100vw" (quirks mode)] expected: FAIL - [ ref sizes="100vw" (quirks mode)] - expected: FAIL - [ ref sizes="100vw" (quirks mode)] expected: FAIL @@ -540,18 +528,12 @@ [ ref sizes="1px" (display:none)] expected: FAIL - [ ref sizes="100vw" (display:none)] - expected: FAIL - [ ref sizes="100vw" (display:none)] expected: FAIL [ ref sizes="100vw" (display:none)] expected: FAIL - [ ref sizes="100vw" (display:none)] - expected: FAIL - [ ref sizes="100vw" (display:none)] expected: FAIL @@ -726,18 +708,12 @@ [ ref sizes="1px" (width:1000px)] expected: FAIL - [ ref sizes="100vw" (width:1000px)] - expected: FAIL - [ ref sizes="100vw" (width:1000px)] expected: FAIL [ ref sizes="100vw" (width:1000px)] expected: FAIL - [ ref sizes="100vw" (width:1000px)] - expected: FAIL - [ ref sizes="100vw" (width:1000px)] expected: FAIL diff --git a/testing/web-platform/meta/url/a-element.html.ini b/testing/web-platform/meta/url/a-element.html.ini index b707d5e069..287738526b 100644 --- a/testing/web-platform/meta/url/a-element.html.ini +++ b/testing/web-platform/meta/url/a-element.html.ini @@ -153,9 +153,6 @@ [Parsing: against ] expected: FAIL - [Parsing: against ] - expected: FAIL - [Parsing: against ] expected: FAIL diff --git a/testing/web-platform/meta/url/url-constructor.html.ini b/testing/web-platform/meta/url/url-constructor.html.ini index 6aeee9a1fa..94547d7793 100644 --- a/testing/web-platform/meta/url/url-constructor.html.ini +++ b/testing/web-platform/meta/url/url-constructor.html.ini @@ -108,9 +108,6 @@ [Parsing: against ] expected: FAIL - [Parsing: against ] - expected: FAIL - [Parsing: against ] expected: FAIL diff --git a/testing/web-platform/meta/web-animations/animation-model/animation-types/not-animatable.html.ini b/testing/web-platform/meta/web-animations/animation-model/animation-types/not-animatable.html.ini new file mode 100644 index 0000000000..6e3530a1f3 --- /dev/null +++ b/testing/web-platform/meta/web-animations/animation-model/animation-types/not-animatable.html.ini @@ -0,0 +1,10 @@ +[not-animatable.html] + type: testharness + [CSS animations and CSS transitions properties cannot be animated using property-indexed notation] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1264822 + + [CSS animations and CSS transitions properties cannot be animated using a sequence of keyframes] + expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1264822 + diff --git a/testing/web-platform/tests/web-animations/animation-model/animation-types/not-animatable.html b/testing/web-platform/tests/web-animations/animation-model/animation-types/not-animatable.html new file mode 100644 index 0000000000..653c78036e --- /dev/null +++ b/testing/web-platform/tests/web-animations/animation-model/animation-types/not-animatable.html @@ -0,0 +1,120 @@ + + +Tests for not animatable properties + + + + + +
+ diff --git a/toolkit/components/formautofill/FormAutofill.jsm b/toolkit/components/formautofill/FormAutofill.jsm index e87431b0c3..aae3a956ca 100644 --- a/toolkit/components/formautofill/FormAutofill.jsm +++ b/toolkit/components/formautofill/FormAutofill.jsm @@ -14,31 +14,13 @@ this.EXPORTED_SYMBOLS = [ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillIntegration", - "resource://gre/modules/FormAutofillIntegration.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Task", - "resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/Integration.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); /** * Main module handling references to objects living in the main process. */ this.FormAutofill = { - /** - * Dynamically generated object implementing the FormAutofillIntegration - * methods. Platform-specific code and add-ons can override methods of this - * object using the registerIntegration method. - */ - get integration() { - // This lazy getter is only called if registerIntegration was never called. - this._refreshIntegrations(); - return this.integration; - }, - /** * Registers new overrides for the FormAutofillIntegration methods. Example: * @@ -57,9 +39,8 @@ this.FormAutofill = { * integration functions changes. Thus, it should not have any side * effects or do any other initialization. */ - registerIntegration: function (aIntegrationFn) { - this._integrationFns.add(aIntegrationFn); - this._refreshIntegrations(); + registerIntegration(aIntegrationFn) { + Integration.formAutofill.register(aIntegrationFn); }, /** @@ -72,45 +53,8 @@ this.FormAutofill = { * @param aIntegrationFn * This must be the same function object passed to registerIntegration. */ - unregisterIntegration: function (aIntegrationFn) { - this._integrationFns.delete(aIntegrationFn); - this._refreshIntegrations(); - }, - - /** - * Ordered list of registered functions defining integration overrides. - */ - _integrationFns: new Set(), - - /** - * Updates the "integration" getter with the object resulting from combining - * all the registered integration overrides with the default implementation. - */ - _refreshIntegrations: function () { - delete this.integration; - - let combined = FormAutofillIntegration; - for (let integrationFn of this._integrationFns) { - try { - // Obtain a new set of methods from the next integration function in the - // list, specifying the current combined object as the base argument. - let integration = integrationFn.call(null, combined); - - // Retrieve a list of property descriptors from the returned object, and - // use them to build a new combined object whose prototype points to the - // previous combined object. - let descriptors = {}; - for (let name of Object.getOwnPropertyNames(integration)) { - descriptors[name] = Object.getOwnPropertyDescriptor(integration, name); - } - combined = Object.create(combined, descriptors); - } catch (ex) { - // Any error will result in the current integration being skipped. - Cu.reportError(ex); - } - } - - this.integration = combined; + unregisterIntegration(aIntegrationFn) { + Integration.formAutofill.unregister(aIntegrationFn); }, /** @@ -127,3 +71,15 @@ this.FormAutofill = { return yield ui.show(); }), }; + +/** + * Dynamically generated object implementing the FormAutofillIntegration + * methods. Platform-specific code and add-ons can override methods of this + * object using the registerIntegration method. + */ +Integration.formAutofill.defineModuleGetter( + this.FormAutofill, + "integration", + "resource://gre/modules/FormAutofillIntegration.jsm", + "FormAutofillIntegration" +); diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index fe88f5b01c..dad9693c82 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -2796,6 +2796,11 @@ "kind": "boolean", "description": "The URL path contains !//" }, + "NETWORK_AUTODIAL": { + "expires_in_version": "never", + "kind": "boolean", + "description": "session used autodialer" + }, "NETWORK_SESSION_AT_256FD": { "expires_in_version": "49", "kind": "boolean", @@ -3369,6 +3374,29 @@ "kind": "boolean", "description": "Did UrlClassifier fail to construct the PrefixSet?" }, + "URLCLASSIFIER_UPDATE_REMOTE_STATUS": { + "alert_emails": ["gcp@mozilla.com", "francois@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 16, + "bug_numbers": [1150921], + "description": "Server HTTP status code from SafeBrowsing database updates. (0=1xx, 1=200, 2=2xx, 3=204, 4=3xx, 5=400, 6=4xx, 7=403, 8=404, 9=408, 10=413, 11=5xx, 12=502|504|511, 13=503, 14=505, 15=Other)" + }, + "URLCLASSIFIER_COMPLETE_REMOTE_STATUS": { + "alert_emails": ["gcp@mozilla.com", "francois@mozilla.com"], + "expires_in_version": "never", + "kind": "enumerated", + "n_values": 16, + "bug_numbers": [1150921], + "description": "Server HTTP status code from remote SafeBrowsing gethash lookups. (0=1xx, 1=200, 2=2xx, 3=204, 4=3xx, 5=400, 6=4xx, 7=403, 8=404, 9=408, 10=413, 11=5xx, 12=502|504|511, 13=503, 14=505, 15=Other)" + }, + "URLCLASSIFIER_COMPLETE_TIMEOUT": { + "alert_emails": ["gcp@mozilla.com", "francois@mozilla.com"], + "expires_in_version": "52", + "kind": "boolean", + "bug_numbers": [1172688], + "description": "This metric is recorded every time a gethash lookup is performed, `true` is recorded if the lookup times out." + }, "CSP_DOCUMENTS_COUNT": { "alert_emails": ["seceng@mozilla.com"], "bug_numbers": [1252829], diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index 0220005e07..e3112eb37d 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -863,6 +863,8 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) } } + LOG(("nsUrlClassifierLookupCallback::LookupComplete [%p] " + "%u pending completions", this, mPendingCompletions)); if (mPendingCompletions == 0) { // All results were complete, we're ready! HandleResults(); @@ -933,8 +935,14 @@ nsUrlClassifierLookupCallback::HandleResults() { if (!mResults) { // No results, this URI is clean. + LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, no results]", this)); return mCallback->HandleEvent(NS_LITERAL_CSTRING("")); } + MOZ_ASSERT(mPendingCompletions == 0, "HandleResults() should never be " + "called while there are pending completions"); + + LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, %u results]", + this, mResults->Length())); nsTArray tables; // Build a stringified list of result tables. @@ -952,7 +960,8 @@ nsUrlClassifierLookupCallback::HandleResults() continue; } - LOG(("Confirmed result from table %s", result.mTableName.get())); + LOG(("Confirmed result %X from table %s", + result.hash.prefix.ToUint32(), result.mTableName.get())); if (tables.IndexOf(result.mTableName) == nsTArray::NoIndex) { tables.AppendElement(result.mTableName); diff --git a/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js b/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js index a7900b84cf..03c9c6b83d 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js +++ b/toolkit/components/url-classifier/nsUrlClassifierHashCompleter.js @@ -47,6 +47,115 @@ function log(...stuff) { dump(msg + "\n"); } +// Map the HTTP response code to a Telemetry bucket +// https://developers.google.com/safe-browsing/developers_guide_v2?hl=en +function httpStatusToBucket(httpStatus) { + var statusBucket; + switch (httpStatus) { + case 100: + case 101: + // Unexpected 1xx return code + statusBucket = 0; + break; + case 200: + // OK - Data is available in the HTTP response body. + statusBucket = 1; + break; + case 201: + case 202: + case 203: + case 205: + case 206: + // Unexpected 2xx return code + statusBucket = 2; + break; + case 204: + // No Content - There are no full-length hashes with the requested prefix. + statusBucket = 3; + break; + case 300: + case 301: + case 302: + case 303: + case 304: + case 305: + case 307: + case 308: + // Unexpected 3xx return code + statusBucket = 4; + break; + case 400: + // Bad Request - The HTTP request was not correctly formed. + // The client did not provide all required CGI parameters. + statusBucket = 5; + break; + case 401: + case 402: + case 405: + case 406: + case 407: + case 409: + case 410: + case 411: + case 412: + case 414: + case 415: + case 416: + case 417: + case 421: + case 426: + case 428: + case 429: + case 431: + case 451: + // Unexpected 4xx return code + statusBucket = 6; + break; + case 403: + // Forbidden - The client id is invalid. + statusBucket = 7; + break; + case 404: + // Not Found + statusBucket = 8; + break; + case 408: + // Request Timeout + statusBucket = 9; + break; + case 413: + // Request Entity Too Large - Bug 1150334 + statusBucket = 10; + break; + case 500: + case 501: + case 510: + // Unexpected 5xx return code + statusBucket = 11; + break; + case 502: + case 504: + case 511: + // Local network errors, we'll ignore these. + statusBucket = 12; + break; + case 503: + // Service Unavailable - The server cannot handle the request. + // Clients MUST follow the backoff behavior specified in the + // Request Frequency section. + statusBucket = 13; + break; + case 505: + // HTTP Version Not Supported - The server CANNOT handle the requested + // protocol major version. + statusBucket = 14; + break; + default: + statusBucket = 15; + }; + return statusBucket; +} + function HashCompleter() { // The current HashCompleterRequest in flight. Once it is started, it is set // to null. It may be used by multiple calls to |complete| in succession to @@ -229,6 +338,7 @@ HashCompleterRequest.prototype = { // channel. if (this._channel && this._channel.isPending()) { dump("hashcompleter: cancelling request to " + this.gethashUrl + "\n"); + Services.telemetry.getHistogramById("URLCLASSIFIER_COMPLETE_TIMEOUT").add(1); this._channel.cancel(Cr.NS_BINDING_ABORTED); } }, @@ -433,9 +543,14 @@ HashCompleterRequest.prototype = { aStatusCode = Cr.NS_ERROR_ABORT; } } - log('Received a ' + httpStatus + ' status code from the gethash server.'); - let success = Components.isSuccessCode(aStatusCode); + log('Received a ' + httpStatus + ' status code from the gethash server (success=' + success + ').'); + + let histogram = + Services.telemetry.getHistogramById("URLCLASSIFIER_COMPLETE_REMOTE_STATUS"); + histogram.add(httpStatusToBucket(httpStatus)); + Services.telemetry.getHistogramById("URLCLASSIFIER_COMPLETE_TIMEOUT").add(0); + // Notify the RequestBackoff once a response is received. this._completer.finishRequest(this.gethashUrl, httpStatus); diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp index a8ad3a3445..431c2a3513 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp @@ -20,6 +20,7 @@ #include "mozilla/Logging.h" #include "nsIInterfaceRequestor.h" #include "mozilla/LoadContext.h" +#include "mozilla/Telemetry.h" #include "nsContentUtils.h" static const char* gQuitApplicationMessage = "quit-application"; @@ -444,6 +445,114 @@ nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody) return NS_OK; } +// Map the HTTP response code to a Telemetry bucket +static uint32_t HTTPStatusToBucket(uint32_t status) +{ + uint32_t statusBucket; + switch (status) { + case 100: + case 101: + // Unexpected 1xx return code + statusBucket = 0; + break; + case 200: + // OK - Data is available in the HTTP response body. + statusBucket = 1; + break; + case 201: + case 202: + case 203: + case 205: + case 206: + // Unexpected 2xx return code + statusBucket = 2; + break; + case 204: + // No Content + statusBucket = 3; + break; + case 300: + case 301: + case 302: + case 303: + case 304: + case 305: + case 307: + case 308: + // Unexpected 3xx return code + statusBucket = 4; + break; + case 400: + // Bad Request - The HTTP request was not correctly formed. + // The client did not provide all required CGI parameters. + statusBucket = 5; + break; + case 401: + case 402: + case 405: + case 406: + case 407: + case 409: + case 410: + case 411: + case 412: + case 414: + case 415: + case 416: + case 417: + case 421: + case 426: + case 428: + case 429: + case 431: + case 451: + // Unexpected 4xx return code + statusBucket = 6; + break; + case 403: + // Forbidden - The client id is invalid. + statusBucket = 7; + break; + case 404: + // Not Found + statusBucket = 8; + break; + case 408: + // Request Timeout + statusBucket = 9; + break; + case 413: + // Request Entity Too Large - Bug 1150334 + statusBucket = 10; + break; + case 500: + case 501: + case 510: + // Unexpected 5xx return code + statusBucket = 11; + break; + case 502: + case 504: + case 511: + // Local network errors, we'll ignore these. + statusBucket = 12; + break; + case 503: + // Service Unavailable - The server cannot handle the request. + // Clients MUST follow the backoff behavior specified in the + // Request Frequency section. + statusBucket = 13; + break; + case 505: + // HTTP Version Not Supported - The server CANNOT handle the requested + // protocol major version. + statusBucket = 14; + break; + default: + statusBucket = 15; + }; + return statusBucket; +} /////////////////////////////////////////////////////////////////////////////// // nsIStreamListenerObserver implementation @@ -479,6 +588,9 @@ nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request, if (NS_FAILED(status)) { // Assume we're overloading the server and trigger backoff. downloadError = true; + mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS, + 15 /* unknown response code */); + } else { bool succeeded = false; rv = httpChannel->GetRequestSucceeded(&succeeded); @@ -487,6 +599,8 @@ nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request, uint32_t requestStatus; rv = httpChannel->GetResponseStatus(&requestStatus); NS_ENSURE_SUCCESS(rv, rv); + mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS, + HTTPStatusToBucket(requestStatus)); LOG(("nsUrlClassifierStreamUpdater::OnStartRequest %s (%d)", succeeded ? "succeeded" : "failed", requestStatus)); if (!succeeded) { diff --git a/toolkit/modules/Integration.jsm b/toolkit/modules/Integration.jsm new file mode 100644 index 0000000000..d648d80ea1 --- /dev/null +++ b/toolkit/modules/Integration.jsm @@ -0,0 +1,283 @@ +/* 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/. */ + +/* + * Implements low-overhead integration between components of the application. + * This may have different uses depending on the component, including: + * + * - Providing product-specific implementations registered at startup. + * - Using alternative implementations during unit tests. + * - Allowing add-ons to change specific behaviors. + * + * Components may define one or more integration points, each defined by a + * root integration object whose properties and methods are the public interface + * and default implementation of the integration point. For example: + * + * const DownloadIntegration = { + * getTemporaryDirectory() { + * return "/tmp/"; + * }, + * + * getTemporaryFile(name) { + * return this.getTemporaryDirectory() + name; + * }, + * }; + * + * Other parts of the application may register overrides for some or all of the + * defined properties and methods. The component defining the integration point + * does not have to be loaded at this stage, because the name of the integration + * point is the only information required. For example, if the integration point + * is called "downloads": + * + * Integration.downloads.register(base => ({ + * getTemporaryDirectory() { + * return base.getTemporaryDirectory.call(this) + "subdir/"; + * }, + * })); + * + * When the component defining the integration point needs to call a method on + * the integration object, instead of using it directly the component would use + * the "getCombined" method to retrieve an object that includes all overrides. + * For example: + * + * let combined = Integration.downloads.getCombined(DownloadIntegration); + * Assert.is(combined.getTemporaryFile("file"), "/tmp/subdir/file"); + * + * Overrides can be registered at startup or at any later time, so each call to + * "getCombined" may return a different object. The simplest way to create a + * reference to the combined object that stays updated to the latest version is + * to define the root object in a JSM and use the "defineModuleGetter" method. + * + * *** Registration *** + * + * Since the interface is not declared formally, the registrations can happen + * at startup without loading the component, so they do not affect performance. + * + * Hovever, this module does not provide a startup registry, this means that the + * code that registers and implements the override must be loaded at startup. + * + * If performance for the override code is a concern, you can take advantage of + * the fact that the function used to create the override is called lazily, and + * include only a stub loader for the final code in an existing startup module. + * + * The registration of overrides should be repeated for each process where the + * relevant integration methods will be called. + * + * *** Accessing base methods and properties *** + * + * Overrides are included in the prototype chain of the combined object in the + * same order they were registered, where the first is closest to the root. + * + * When defining overrides, you do not need to set the "__proto__" property of + * the objects you create, because their properties and methods are moved to a + * new object with the correct prototype. If you do, however, you can call base + * properties and methods using the "super" keyword. For example: + * + * Integration.downloads.register(base => ({ + * __proto__: base, + * getTemporaryDirectory() { + * return super.getTemporaryDirectory() + "subdir/"; + * }, + * })); + * + * *** State handling *** + * + * Storing state directly on the combined integration object using the "this" + * reference is not recommended. When a new integration is registered, own + * properties stored on the old combined object are copied to the new combined + * object using a shallow copy, but the "this" reference for new invocations + * of the methods will be different. + * + * If the root object defines a property that always points to the same object, + * for example a "state" property, you can safely use it across registrations. + * + * Integration overrides provided by restartless add-ons should not use the + * "this" reference to store state, to avoid conflicts with other add-ons. + * + * *** Interaction with XPCOM *** + * + * Providing the combined object as an argument to any XPCOM method will + * generate a console error message, and will throw an exception where possible. + * For example, you cannot register observers directly on the combined object. + * This helps preventing mistakes due to the fact that the combined object + * reference changes when new integration overrides are registered. + */ + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "Integration", +]; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +/** + * Maps integration point names to IntegrationPoint objects. + */ +const gIntegrationPoints = new Map(); + +/** + * This Proxy object creates IntegrationPoint objects using their name as key. + * The objects will be the same for the duration of the process. For example: + * + * Integration.downloads.register(...); + * Integration["addon-provided-integration"].register(...); + */ +this.Integration = new Proxy({}, { + get(target, name) { + let integrationPoint = gIntegrationPoints.get(name); + if (!integrationPoint) { + integrationPoint = new IntegrationPoint(); + gIntegrationPoints.set(name, integrationPoint); + } + return integrationPoint; + }, +}); + +/** + * Individual integration point for which overrides can be registered. + */ +this.IntegrationPoint = function () { + this._overrideFns = new Set(); + this._combined = { + QueryInterface: function() { + let ex = new Components.Exception( + "Integration objects should not be used with XPCOM because" + + " they change when new overrides are registered.", + Cr.NS_ERROR_NO_INTERFACE); + Cu.reportError(ex); + throw ex; + }, + }; +} + +this.IntegrationPoint.prototype = { + /** + * Ordered set of registered functions defining integration overrides. + */ + _overrideFns: null, + + /** + * Combined integration object. When this reference changes, properties + * defined directly on this object are copied to the new object. + * + * Initially, the only property of this object is a "QueryInterface" method + * that throws an exception, to prevent misuse as a permanent XPCOM listener. + */ + _combined: null, + + /** + * Indicates whether the integration object is current based on the list of + * registered integration overrides. + */ + _combinedIsCurrent: false, + + /** + * Registers new overrides for the integration methods. For example: + * + * Integration.nameOfIntegrationPoint.register(base => ({ + * asyncMethod: Task.async(function* () { + * return yield base.asyncMethod.apply(this, arguments); + * }), + * })); + * + * @param overrideFn + * Function returning an object defining the methods that should be + * overridden. Its only parameter is an object that contains the base + * implementation of all the available methods. + * + * @note The override function is called every time the list of registered + * override functions changes. Thus, it should not have any side + * effects or do any other initialization. + */ + register(overrideFn) { + this._overrideFns.add(overrideFn); + this._combinedIsCurrent = false; + }, + + /** + * Removes a previously registered integration override. + * + * Overrides don't usually need to be unregistered, unless they are added by a + * restartless add-on, in which case they should be unregistered when the + * add-on is disabled or uninstalled. + * + * @param overrideFn + * This must be the same function object passed to "register". + */ + unregister(overrideFn) { + this._overrideFns.delete(overrideFn); + this._combinedIsCurrent = false; + }, + + /** + * Retrieves the dynamically generated object implementing the integration + * methods. Platform-specific code and add-ons can override methods of this + * object using the "register" method. + */ + getCombined(root) { + if (this._combinedIsCurrent) { + return this._combined; + } + + // In addition to enumerating all the registered integration overrides in + // order, we want to keep any state that was previously stored in the + // combined object using the "this" reference in integration methods. + let overrideFnArray = [...this._overrideFns, () => this._combined]; + + let combined = root; + for (let overrideFn of overrideFnArray) { + try { + // Obtain a new set of methods from the next override function in the + // list, specifying the current combined object as the base argument. + let override = overrideFn.call(null, combined); + + // Retrieve a list of property descriptors from the returned object, and + // use them to build a new combined object whose prototype points to the + // previous combined object. + let descriptors = {}; + for (let name of Object.getOwnPropertyNames(override)) { + descriptors[name] = Object.getOwnPropertyDescriptor(override, name); + } + combined = Object.create(combined, descriptors); + } catch (ex) { + // Any error will result in the current override being skipped. + Cu.reportError(ex); + } + } + + this._combinedIsCurrent = true; + return this._combined = combined; + }, + + /** + * Defines a getter to retrieve the dynamically generated object implementing + * the integration methods, loading the root implementation lazily from the + * specified JSM module. For example: + * + * Integration.test.defineModuleGetter(this, "TestIntegration", + * "resource://testing-common/TestIntegration.jsm"); + * + * @param targetObject + * The object on which the lazy getter will be defined. + * @param name + * The name of the getter to define. + * @param moduleUrl + * The URL used to obtain the module. + * @param symbol [optional] + * The name of the symbol exported by the module. This can be omitted + * if the name of the exported symbol is equal to the getter name. + */ + defineModuleGetter(targetObject, name, moduleUrl, symbol) { + let moduleHolder = {}; + XPCOMUtils.defineLazyModuleGetter(moduleHolder, name, moduleUrl, symbol); + Object.defineProperty(targetObject, name, { + get: () => this.getCombined(moduleHolder[name]), + configurable: true, + enumerable: true, + }); + }, +}; diff --git a/toolkit/modules/PageMetadata.jsm b/toolkit/modules/PageMetadata.jsm index 44e0eade2e..820ec38eab 100644 --- a/toolkit/modules/PageMetadata.jsm +++ b/toolkit/modules/PageMetadata.jsm @@ -10,6 +10,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/microformat-shiv.js"); XPCOMUtils.defineLazyServiceGetter(this, "UnescapeService", "@mozilla.org/feed-unescapehtml;1", @@ -25,7 +26,7 @@ const DISCOVER_IMAGES_MAX = 5; /** - * Extract metadata and microdata from a HTML document. + * Extract metadata and microformats from a HTML document. * @type {Object} */ this.PageMetadata = { @@ -36,14 +37,14 @@ this.PageMetadata = { * - Metadata specified in tags, including OpenGraph data * - Links specified in tags (short, canonical, preview images, alternative) * - Content that can be found in the page content that we consider useful metadata - * - Microdata, as defined by the spec: - * http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata-2.html + * - Microformats * * @param {Document} document - Document to extract data from. + * @param {Element} [target] - Optional element to restrict microformats lookup to. * @returns {Object} Object containing the various metadata, normalized to * merge some common alternative names for metadata. */ - getData(document) { + getData(document, target = null) { let result = { url: this._validateURL(document, document.documentURI), title: document.title, @@ -68,84 +69,16 @@ this.PageMetadata = { this._getMetaData(document, result); this._getLinkData(document, result); this._getPageData(document, result); - result.microdata = this.getMicrodata(document); + result.microformats = this.getMicroformats(document, target); return result; }, - /** - * Get all microdata from an HTML document, or from only a given element. - * - * Returns an object in the format: - * { - * "items": [ - * { - * "type": [ "" ], - * "properties": { - * "": [ "", ... ], - * ..., - * } - * }, - * ..., - * ] - * } - * - * @note This is based on wg algorythm to convert microdata to json - * http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata-2.html#json - * - * @param {Document} document - Document to extract data from. - * @param {Element} [target] - Optional element to restrict microdata lookup to. - * @return {Object} Object describing the found microdata. - */ - getMicrodata(document, target = null) { - function getObject(item) { - let result = {}; - - if (item.itemType.length) { - result.types = [...item.itemType]; - } - - if (item.itemId) { - result.itemId = item.itemId; - } - - if (item.properties.length) { - result.properties = {}; - } - - for (let elem of item.properties) { - let value; - if (elem.itemScope) { - value = getObject(elem); - } else if (elem.itemValue) { - value = elem.itemValue; - } else if (elem.hasAttribute("content")) { - // Handle mis-formatted microdata. - value = elem.getAttribute("content"); - } - - for (let prop of elem.itemProp) { - if (!result.properties[prop]) { - result.properties[prop] = []; - } - - result.properties[prop].push(value); - } - } - - return result; + getMicroformats(document, target = null) { + if (target) { + return Microformats.getParent(target, {node: document}); } - - let result = { items: [] }; - let elements = target ? [target] : document.getItems(); - - for (let element of elements) { - if (element.itemScope) { - result.items.push(getObject(element)); - } - } - - return result; + return Microformats.get({node: document}); }, /** diff --git a/toolkit/modules/PromiseMessage.jsm b/toolkit/modules/PromiseMessage.jsm index 8419ccb22f..f232d074b3 100644 --- a/toolkit/modules/PromiseMessage.jsm +++ b/toolkit/modules/PromiseMessage.jsm @@ -10,14 +10,11 @@ var msgId = 0; var PromiseMessage = { send(messageManager, name, data = {}) { - let id = msgId++; + const id = `${name}_${msgId++}`; - // Make a copy of data so that the caller doesn't see us setting 'id'. - let dataCopy = {}; - for (let prop in data) { - dataCopy[prop] = data[prop]; - } - dataCopy.id = id; + // Make a copy of data so that the caller doesn't see us setting 'id': + // To a new object, assign data's props, and then override the id. + const dataCopy = Object.assign({}, data, {id}); // Send the message. messageManager.sendAsyncMessage(name, dataCopy); diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index dc7d5855c8..ada0f07738 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -11,6 +11,7 @@ MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini'] TESTING_JS_MODULES += [ 'tests/PromiseTestUtils.jsm', + 'tests/xpcshell/TestIntegration.jsm', ] SPHINX_TREES['toolkit_modules'] = 'docs' @@ -41,6 +42,7 @@ EXTRA_JS_MODULES += [ 'Http.jsm', 'InlineSpellChecker.jsm', 'InlineSpellCheckerContent.jsm', + 'Integration.jsm', 'LoadContextInfo.jsm', 'Locale.jsm', 'Log.jsm', diff --git a/toolkit/modules/tests/browser/browser.ini b/toolkit/modules/tests/browser/browser.ini index 63c8997a21..f10d688507 100644 --- a/toolkit/modules/tests/browser/browser.ini +++ b/toolkit/modules/tests/browser/browser.ini @@ -18,5 +18,6 @@ skip-if = e10s # Bug ?????? - test already uses content scripts, but still fails [browser_WebRequest_cookies.js] [browser_WebRequest_filtering.js] [browser_PageMetadata.js] +[browser_PromiseMessage.js] [browser_RemotePageManager.js] [browser_Troubleshoot.js] diff --git a/toolkit/modules/tests/browser/browser_PromiseMessage.js b/toolkit/modules/tests/browser/browser_PromiseMessage.js new file mode 100644 index 0000000000..89d437c66a --- /dev/null +++ b/toolkit/modules/tests/browser/browser_PromiseMessage.js @@ -0,0 +1,38 @@ +/* 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/. */ +/*global Cu, BrowserTestUtils, is, ok, add_task, gBrowser */ +"use strict"; +Cu.import("resource://gre/modules/PromiseMessage.jsm", this); + + +const url = "http://example.org/tests/dom/manifest/test/resource.sjs"; + +/** + * Test basic API error conditions + */ +add_task(function* () { + yield BrowserTestUtils.withNewTab({gBrowser, url}, testPromiseMessageAPI) +}); + +function* testPromiseMessageAPI(aBrowser){ + // Reusing an existing message. + const msgKey = "DOM:WebManifest:hasManifestLink"; + const mm = aBrowser.messageManager; + const id = "this should not change"; + const foo = "neitherShouldThis"; + const data = {id, foo}; + + // This just returns false, and it doesn't matter for this test. + yield PromiseMessage.send(mm, msgKey, data); + + // Check that no new props were added + const props = Object.getOwnPropertyNames(data); + ok(props.length === 2, "There should only be 2 props"); + ok(props.includes("id"), "Has the id property"); + ok(props.includes("foo"), "Has the foo property"); + + // Check that the props didn't change. + is(data.id, id, "The id prop must not change."); + is(data.foo, foo, "The foo prop must not change."); +} diff --git a/toolkit/modules/tests/xpcshell/TestIntegration.jsm b/toolkit/modules/tests/xpcshell/TestIntegration.jsm new file mode 100644 index 0000000000..78a0b72671 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/TestIntegration.jsm @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Internal module used to test the generation of Integration.jsm getters. + */ + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "TestIntegration", +]; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/Task.jsm"); + +this.TestIntegration = { + value: "value", + + get valueFromThis() { + return this.value; + }, + + get property() { + return this._property; + }, + + set property(value) { + this._property = value; + }, + + method(argument) { + this.methodArgument = argument; + return "method" + argument; + }, + + asyncMethod: Task.async(function* (argument) { + this.asyncMethodArgument = argument; + return "asyncMethod" + argument; + }), +}; diff --git a/toolkit/modules/tests/xpcshell/test_Integration.js b/toolkit/modules/tests/xpcshell/test_Integration.js new file mode 100644 index 0000000000..808e2d34fb --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_Integration.js @@ -0,0 +1,238 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Tests the Integration.jsm module. + */ + +"use strict"; + +const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; + +Cu.import("resource://gre/modules/Integration.jsm", this); +Cu.import("resource://gre/modules/Services.jsm", this); +Cu.import("resource://gre/modules/Task.jsm", this); + +const TestIntegration = { + value: "value", + + get valueFromThis() { + return this.value; + }, + + get property() { + return this._property; + }, + + set property(value) { + this._property = value; + }, + + method(argument) { + this.methodArgument = argument; + return "method" + argument; + }, + + asyncMethod: Task.async(function* (argument) { + this.asyncMethodArgument = argument; + return "asyncMethod" + argument; + }), +}; + +let overrideFn = base => ({ + value: "overridden-value", + + get property() { + return "overridden-" + base.__lookupGetter__("property").call(this); + }, + + set property(value) { + base.__lookupSetter__("property").call(this, "overridden-" + value); + }, + + method() { + return "overridden-" + base.method.apply(this, arguments); + }, + + asyncMethod: Task.async(function* () { + return "overridden-" + (yield base.asyncMethod.apply(this, arguments)); + }), +}); + +let superOverrideFn = base => ({ + __proto__: base, + + value: "overridden-value", + + get property() { + return "overridden-" + super.property; + }, + + set property(value) { + super.property = "overridden-" + value; + }, + + method() { + return "overridden-" + super.method(...arguments); + }, + + asyncMethod: Task.async(function* () { + // We cannot use the "super" keyword in methods defined using "Task.async". + return "overridden-" + (yield base.asyncMethod.apply(this, arguments)); + }), +}); + +/** + * Fails the test if the results of method invocations on the combined object + * don't match the expected results based on how many overrides are registered. + * + * @param combined + * The combined object based on the TestIntegration root. + * @param overridesCount + * Zero if the root object is not overridden, or a higher value to test + * the presence of one or more integration overrides. + */ +function* assertCombinedResults(combined, overridesCount) { + let expectedValue = overridesCount > 0 ? "overridden-value" : "value"; + let prefix = "overridden-".repeat(overridesCount); + + Assert.equal(combined.value, expectedValue); + Assert.equal(combined.valueFromThis, expectedValue); + + combined.property = "property"; + Assert.equal(combined.property, prefix.repeat(2) + "property"); + + combined.methodArgument = ""; + Assert.equal(combined.method("-argument"), prefix + "method-argument"); + Assert.equal(combined.methodArgument, "-argument"); + + combined.asyncMethodArgument = ""; + Assert.equal(yield combined.asyncMethod("-argument"), + prefix + "asyncMethod-argument"); + Assert.equal(combined.asyncMethodArgument, "-argument"); +} + +/** + * Fails the test if the results of method invocations on the combined object + * for the "testModule" integration point don't match the expected results based + * on how many overrides are registered. + * + * @param overridesCount + * Zero if the root object is not overridden, or a higher value to test + * the presence of one or more integration overrides. + */ +function* assertCurrentCombinedResults(overridesCount) { + let combined = Integration.testModule.getCombined(TestIntegration); + yield assertCombinedResults(combined, overridesCount); +} + +/** + * Checks the initial state with no integration override functions registered. + */ +add_task(function* test_base() { + yield assertCurrentCombinedResults(0); +}); + +/** + * Registers and unregisters an integration override function. + */ +add_task(function* test_override() { + Integration.testModule.register(overrideFn); + yield assertCurrentCombinedResults(1); + + // Registering the same function more than once has no effect. + Integration.testModule.register(overrideFn); + yield assertCurrentCombinedResults(1); + + Integration.testModule.unregister(overrideFn); + yield assertCurrentCombinedResults(0); +}); + +/** + * Registers and unregisters more than one integration override function, of + * which one uses the prototype and the "super" keyword to access the base. + */ +add_task(function* test_override_super_multiple() { + Integration.testModule.register(overrideFn); + Integration.testModule.register(superOverrideFn); + yield assertCurrentCombinedResults(2); + + Integration.testModule.unregister(overrideFn); + yield assertCurrentCombinedResults(1); + + Integration.testModule.unregister(superOverrideFn); + yield assertCurrentCombinedResults(0); +}); + +/** + * Registers an integration override function that throws an exception, and + * ensures that this does not block other functions from being registered. + */ +add_task(function* test_override_error() { + let errorOverrideFn = base => { throw "Expected error." }; + + Integration.testModule.register(errorOverrideFn); + Integration.testModule.register(overrideFn); + yield assertCurrentCombinedResults(1); + + Integration.testModule.unregister(errorOverrideFn); + Integration.testModule.unregister(overrideFn); + yield assertCurrentCombinedResults(0); +}); + +/** + * Checks that state saved using the "this" reference is preserved as a shallow + * copy when registering new integration override functions. + */ +add_task(function* test_state_preserved() { + let valueObject = { toString: () => "toString" }; + + let combined = Integration.testModule.getCombined(TestIntegration); + combined.property = valueObject; + Assert.ok(combined.property === valueObject); + + Integration.testModule.register(overrideFn); + combined = Integration.testModule.getCombined(TestIntegration); + Assert.equal(combined.property, "overridden-toString"); + + Integration.testModule.unregister(overrideFn); + combined = Integration.testModule.getCombined(TestIntegration); + Assert.ok(combined.property === valueObject); +}); + +/** + * Checks that the combined integration objects cannot be used with XPCOM. + * + * This is limited by the fact that interfaces with the "[function]" annotation, + * for example nsIObserver, do not call the QueryInterface implementation. + */ +add_task(function* test_xpcom_throws() { + let combined = Integration.testModule.getCombined(TestIntegration); + + // This calls QueryInterface because it looks for nsISupportsWeakReference. + Assert.throws(() => Services.obs.addObserver(combined, "test-topic", true), + "NS_NOINTERFACE"); +}); + +/** + * Checks that getters defined by defineModuleGetter are able to retrieve the + * latest version of the combined integration object. + */ +add_task(function* test_defineModuleGetter() { + let objectForGetters = {}; + + // Test with and without the optional "symbol" parameter. + Integration.testModule.defineModuleGetter(objectForGetters, + "TestIntegration", "resource://testing-common/TestIntegration.jsm"); + Integration.testModule.defineModuleGetter(objectForGetters, + "integration", "resource://testing-common/TestIntegration.jsm", + "TestIntegration"); + + Integration.testModule.register(overrideFn); + yield assertCombinedResults(objectForGetters.integration, 1); + yield assertCombinedResults(objectForGetters.TestIntegration, 1); + + Integration.testModule.unregister(overrideFn); + yield assertCombinedResults(objectForGetters.integration, 0); + yield assertCombinedResults(objectForGetters.TestIntegration, 0); +}); diff --git a/toolkit/modules/tests/xpcshell/xpcshell.ini b/toolkit/modules/tests/xpcshell/xpcshell.ini index dcc945935b..f083bd9c06 100644 --- a/toolkit/modules/tests/xpcshell/xpcshell.ini +++ b/toolkit/modules/tests/xpcshell/xpcshell.ini @@ -20,6 +20,7 @@ skip-if = toolkit == 'android' [test_GMPInstallManager.js] skip-if = toolkit == 'android' [test_Http.js] +[test_Integration.js] skip-if = toolkit == 'android' [test_Log.js] skip-if = toolkit == 'android'