From b2f351d94dc168c31f565084a1e57b0811618748 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Wed, 19 Dec 2012 09:28:59 -0800 Subject: [PATCH 001/217] Bug 681805 - Move forward button styling to style files. r=lucasr --- .../layout-land-v14/browser_toolbar.xml.in | 13 ++++-------- .../browser_toolbar_menu.xml.in | 13 ++++-------- .../browser_toolbar_menu.xml.in | 20 ++++--------------- .../browser_toolbar_menu.xml.in | 20 ++++--------------- .../resources/layout/browser_toolbar.xml.in | 13 ++++-------- .../layout/browser_toolbar_menu.xml.in | 13 ++++-------- .../resources/values-large-v11/dimens.xml | 1 + .../resources/values-large-v11/styles.xml | 20 +++++++++++++++++++ .../resources/values-xlarge-v11/dimens.xml | 1 + .../android/base/resources/values/dimens.xml | 1 + .../android/base/resources/values/styles.xml | 13 ++++++++++++ 11 files changed, 60 insertions(+), 68 deletions(-) diff --git a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in index 6539149f2215..68795ca94cea 100644 --- a/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in +++ b/mobile/android/base/resources/layout-land-v14/browser_toolbar.xml.in @@ -16,9 +16,8 @@ android:contentDescription="@string/back" style="@style/AddressBar.ImageButton.Unused"/> - + - + - + - + - + - + - + - + - + - + - + - + 45dp 8dip 26sp + 90dp diff --git a/mobile/android/base/resources/values-large-v11/styles.xml b/mobile/android/base/resources/values-large-v11/styles.xml index 61516db49c1a..75c9d00bc0fb 100644 --- a/mobile/android/base/resources/values-large-v11/styles.xml +++ b/mobile/android/base/resources/values-large-v11/styles.xml @@ -31,4 +31,24 @@ 60dip + + + + diff --git a/mobile/android/base/resources/values-xlarge-v11/dimens.xml b/mobile/android/base/resources/values-xlarge-v11/dimens.xml index e9b2139c6a7f..673492c49947 100644 --- a/mobile/android/base/resources/values-xlarge-v11/dimens.xml +++ b/mobile/android/base/resources/values-xlarge-v11/dimens.xml @@ -16,5 +16,6 @@ 48dp 28dp 26sp + 84dip diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml index 575cbd9c5192..2de91a5914fd 100644 --- a/mobile/android/base/resources/values/dimens.xml +++ b/mobile/android/base/resources/values/dimens.xml @@ -53,6 +53,7 @@ 6dp 8dp + 84dip 632.0dip 598.0dip 632.0dip diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml index 30cea98f0f52..8d3990195945 100644 --- a/mobile/android/base/resources/values/styles.xml +++ b/mobile/android/base/resources/values/styles.xml @@ -65,6 +65,15 @@ @android:color/transparent + + + + diff --git a/mobile/android/base/resources/values/dimens.xml b/mobile/android/base/resources/values/dimens.xml index 2de91a5914fd..13a7cb539d00 100644 --- a/mobile/android/base/resources/values/dimens.xml +++ b/mobile/android/base/resources/values/dimens.xml @@ -53,7 +53,8 @@ 6dp 8dp - 84dip + 90dp + 50dip 632.0dip 598.0dip 632.0dip From 519e58feaa4c080a558ce20048409beb235d8f5a Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Thu, 20 Dec 2012 10:52:33 -0800 Subject: [PATCH 003/217] Bug 823397 - Installing a privileged app from marketplace dev - appStatus should be privileged, but ends up being web. r=fabrice --- dom/apps/src/Webapps.jsm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 546d0552dd3e..14b60db4c141 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -1874,9 +1874,10 @@ this.DOMApplicationRegistry = { : isSigned ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED : Ci.nsIPrincipal.APP_STATUS_INSTALLED; - if (AppsUtils.getAppManifestStatus(aManifest) > maxStatus) { + if (AppsUtils.getAppManifestStatus(manifest) > maxStatus) { throw "INVALID_SECURITY_LEVEL"; } + aApp.appStatus = AppsUtils.getAppManifestStatus(manifest); if (aOnSuccess) { aOnSuccess(id, manifest); From 6c71df8ecf421384b9dc0fa5a3aa79a02f2e1f87 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 20 Dec 2012 12:41:52 -0500 Subject: [PATCH 004/217] Bug 823580 - Remove PlacesUIUtils.privateBrowsing usages in per-window PB builds; r=jdm --HG-- extra : rebase_source : 4b6e55e3da4f57411cf2c3b996adad16ab22d228 --- browser/components/places/content/controller.js | 11 +++++++++-- browser/components/places/jar.mn | 2 +- browser/components/places/src/Makefile.in | 2 +- browser/components/places/src/PlacesUIUtils.jsm | 2 ++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index 6a4609ce8618..32d9ca4511a5 100644 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -141,8 +141,11 @@ PlacesController.prototype = { case "placesCmd_delete": return this._hasRemovableSelection(false); case "placesCmd_deleteDataHost": - return this._hasRemovableSelection(false) && - !PlacesUIUtils.privateBrowsing.privateBrowsingEnabled; + return this._hasRemovableSelection(false) +#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING + && !PlacesUIUtils.privateBrowsing.privateBrowsingEnabled +#endif + ; case "placesCmd_moveBookmarks": return this._hasRemovableSelection(true); case "cmd_copy": @@ -601,8 +604,12 @@ PlacesController.prototype = { // We allow pasting into tag containers, so special case that. var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" && noIp && !(ip && ip.isTag && item.id == "placesContext_paste"); +#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING + var hideIfPB = false; +#else var hideIfPB = item.getAttribute("hideifprivatebrowsing") == "true" && PlacesUIUtils.privateBrowsing.privateBrowsingEnabled; +#endif item.hidden = hideIfNoIP || hideIfPB || !this._shouldShowMenuItem(item, metadata); diff --git a/browser/components/places/jar.mn b/browser/components/places/jar.mn index e8ab80c5246c..389429738051 100644 --- a/browser/components/places/jar.mn +++ b/browser/components/places/jar.mn @@ -15,7 +15,7 @@ browser.jar: content/browser/places/placesOverlay.xul (content/placesOverlay.xul) * content/browser/places/menu.xml (content/menu.xml) content/browser/places/tree.xml (content/tree.xml) - content/browser/places/controller.js (content/controller.js) +* content/browser/places/controller.js (content/controller.js) content/browser/places/treeView.js (content/treeView.js) * content/browser/places/browserPlacesViews.js (content/browserPlacesViews.js) # keep the Places version of the history sidebar at history/history-panel.xul diff --git a/browser/components/places/src/Makefile.in b/browser/components/places/src/Makefile.in index 1d793a3943ef..1a50c1d9a674 100644 --- a/browser/components/places/src/Makefile.in +++ b/browser/components/places/src/Makefile.in @@ -15,7 +15,7 @@ EXTRA_COMPONENTS = \ PlacesProtocolHandler.js \ $(NULL) -EXTRA_JS_MODULES = \ +EXTRA_PP_JS_MODULES = \ PlacesUIUtils.jsm \ $(NULL) diff --git a/browser/components/places/src/PlacesUIUtils.jsm b/browser/components/places/src/PlacesUIUtils.jsm index eb20c6ec77b8..6c3f04f6c5ce 100644 --- a/browser/components/places/src/PlacesUIUtils.jsm +++ b/browser/components/places/src/PlacesUIUtils.jsm @@ -1009,9 +1009,11 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() { Ci.nsIPrefLocalizedString).data; }); +#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "privateBrowsing", "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService"); +#endif XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", "@mozilla.org/docshell/urifixup;1", From 72d59c35ccfc1c849ebd794febe2a8f72474a5e1 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 20 Dec 2012 13:51:01 -0500 Subject: [PATCH 005/217] Bug 823574 - Part 0: Disable browser_social.js in per-window PB builds --HG-- extra : rebase_source : a4279fdbb9c211d33ece9ab8cd4eb002edb71a1e --- browser/base/content/test/social/Makefile.in | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/browser/base/content/test/social/Makefile.in b/browser/base/content/test/social/Makefile.in index 2d26d8a3d0f3..c4b05392d829 100644 --- a/browser/base/content/test/social/Makefile.in +++ b/browser/base/content/test/social/Makefile.in @@ -12,7 +12,6 @@ include $(DEPTH)/config/autoconf.mk _BROWSER_FILES = \ head.js \ - browser_social.js \ browser_social_toolbar.js \ browser_social_shareButton.js \ browser_social_sidebar.js \ @@ -30,6 +29,12 @@ _BROWSER_FILES = \ social_worker.js \ $(NULL) +ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING +_BROWSER_FILES += \ + browser_social.js \ + $(NULL) +endif + include $(topsrcdir)/config/rules.mk libs:: $(_BROWSER_FILES) From 3ccadec3e1f28c23c329b54ed607a58b65bafdc8 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Thu, 20 Dec 2012 09:54:49 -0500 Subject: [PATCH 006/217] Bug 822244 - Use the Downloads View in Places if it's preffed on. r=mak. --- browser/base/content/browser-places.js | 2 +- .../components/downloads/src/DownloadsUI.js | 62 +++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index 262885b374b3..840eec742c22 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -440,7 +440,7 @@ var PlacesCommandHook = { * @param aLeftPaneRoot * The query to select in the organizer window - options * are: History, AllBookmarks, BookmarksMenu, BookmarksToolbar, - * UnfiledBookmarks and Tags. + * UnfiledBookmarks, Tags and Downloads. */ showPlacesOrganizer: function PCH_showPlacesOrganizer(aLeftPaneRoot) { var organizer = Services.wm.getMostRecentWindow("Places:Organizer"); diff --git a/browser/components/downloads/src/DownloadsUI.js b/browser/components/downloads/src/DownloadsUI.js index 6726db795ba6..b7569057c8f9 100644 --- a/browser/components/downloads/src/DownloadsUI.js +++ b/browser/components/downloads/src/DownloadsUI.js @@ -30,6 +30,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", XPCOMUtils.defineLazyServiceGetter(this, "gBrowserGlue", "@mozilla.org/browser/browserglue;1", "nsIBrowserGlue"); +XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", + "resource:///modules/RecentWindow.jsm"); //////////////////////////////////////////////////////////////////////////////// //// DownloadsUI @@ -72,25 +74,29 @@ DownloadsUI.prototype = { let browserWin = gBrowserGlue.getMostRecentBrowserWindow(); if (!browserWin || browserWin.windowState == kMinimized) { - this._toolkitUI.show(aWindowContext, aID, aReason); + this._showDownloadManagerUI(aWindowContext, aID, aReason); } else { // If the indicator is visible, then new download notifications are // already handled by the panel service. browserWin.DownloadsButton.checkIsVisible(function(isVisible) { if (!isVisible) { - this._toolkitUI.show(aWindowContext, aID, aReason); + this._showDownloadManagerUI(aWindowContext, aID, aReason); } }.bind(this)); } } else { - this._toolkitUI.show(aWindowContext, aID, aReason); + this._showDownloadManagerUI(aWindowContext, aID, aReason); } }, get visible() { - return this._toolkitUI.visible; + // If we're still using the toolkit downloads manager, delegate the call + // to it. Otherwise, return true for now, until we decide on how we want + // to indicate that a new download has started if a browser window is + // not available or minimized. + return DownloadsCommon.useToolkitUI ? this._toolkitUI.visible : true; }, getAttention: function DUI_getAttention() @@ -98,6 +104,54 @@ DownloadsUI.prototype = { if (DownloadsCommon.useToolkitUI) { this._toolkitUI.getAttention(); } + }, + + /** + * Helper function that opens the right download manager UI. Either the + * new Downloads View in Places, or the toolkit download window if the + * Places Downloads View is not enabled. + */ + _showDownloadManagerUI: + function DUI_showDownloadManagerUI(aWindowContext, aID, aReason) + { + // First, determine if the Places Downloads view is preffed on. + let usePlacesView = false; + try { + usePlacesView = + Services.prefs.getBoolPref("browser.library.useNewDownloadsView"); + } catch(e) {} + + if (!usePlacesView) { + // If we got here, then the browser.library.useNewDownloadsView pref + // either didn't exist or was false, so just show the toolkit downloads + // manager. + this._toolkitUI.show(aWindowContext, aID, aReason); + return; + } + + let organizer = Services.wm.getMostRecentWindow("Places:Organizer"); + if (!organizer) { + let parentWindow = aWindowContext; + // If we weren't given a window context, try to find a browser window + // to use as our parent - and if that doesn't work, error out and give + // up. + if (!parentWindow) { + parentWindow = RecentWindow.getMostRecentBrowserWindow(); + if (!parentWindow) { + Components.utils + .reportError("Couldn't find a browser window to open " + + "the Places Downloads View from."); + return; + } + } + parentWindow.openDialog("chrome://browser/content/places/places.xul", + "", "chrome,toolbar=yes,dialog=no,resizable", + "Downloads"); + } + else { + organizer.PlacesOrganizer.selectLeftPaneQuery("Downloads"); + organizer.focus(); + } } }; From 4e4a9539b352c3c7b1f22e8fea156c418f3f2c4c Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Thu, 20 Dec 2012 13:27:15 -0500 Subject: [PATCH 007/217] bug 819044 - better spdy stream cleanup when handling goaway r=honzab --HG-- extra : rebase_source : 36de16ee094b4902e942546db3d681d2cdf2a4c0 --- netwerk/protocol/http/SpdySession2.cpp | 60 +++++++++++++++++++++--- netwerk/protocol/http/SpdySession2.h | 7 +++ netwerk/protocol/http/SpdySession3.cpp | 63 ++++++++++++++++++++++---- netwerk/protocol/http/SpdySession3.h | 7 +++ netwerk/protocol/http/SpdyStream2.h | 4 +- netwerk/protocol/http/SpdyStream3.h | 4 +- 6 files changed, 129 insertions(+), 16 deletions(-) diff --git a/netwerk/protocol/http/SpdySession2.cpp b/netwerk/protocol/http/SpdySession2.cpp index 403c26d60107..03cfaafeeadf 100644 --- a/netwerk/protocol/http/SpdySession2.cpp +++ b/netwerk/protocol/http/SpdySession2.cpp @@ -92,9 +92,12 @@ SpdySession2::ShutdownEnumerator(nsAHttpTransaction *key, // On a clean server hangup the server sets the GoAwayID to be the ID of // the last transaction it processed. If the ID of stream in the - // local session is greater than that it can safely be restarted because the - // server guarantees it was not partially processed. - if (self->mCleanShutdown && (stream->StreamID() > self->mGoAwayID)) + // local stream is greater than that it can safely be restarted because the + // server guarantees it was not partially processed. Streams that have not + // registered an ID haven't actually been sent yet so they can always be + // restarted. + if (self->mCleanShutdown && + (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID())) self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted else self->CloseStream(stream, NS_ERROR_ABORT); @@ -102,6 +105,22 @@ SpdySession2::ShutdownEnumerator(nsAHttpTransaction *key, return PL_DHASH_NEXT; } +PLDHashOperator +SpdySession2::GoAwayEnumerator(nsAHttpTransaction *key, + nsAutoPtr &stream, + void *closure) +{ + SpdySession2 *self = static_cast(closure); + + // these streams were not processed by the server and can be restarted. + // Do that after the enumerator completes to avoid the risk of + // a restart event re-entrantly modifying this hash. + if (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID()) + self->mGoAwayStreamsToRestart.Push(stream); + + return PL_DHASH_NEXT; +} + SpdySession2::~SpdySession2() { LOG3(("SpdySession2::~SpdySession2 %p mDownstreamState=%X", @@ -1325,9 +1344,38 @@ SpdySession2::HandleGoAway(SpdySession2 *self) self->mGoAwayID = PR_ntohl(reinterpret_cast(self->mInputFrameBuffer.get())[2]); self->mCleanShutdown = true; - - LOG3(("SpdySession2::HandleGoAway %p GOAWAY Last-Good-ID 0x%X.", - self, self->mGoAwayID)); + + // Find streams greater than the last-good ID and mark them for deletion + // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. They can + // be restarted. + self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self); + + // Process the streams marked for deletion and restart. + uint32_t size = self->mGoAwayStreamsToRestart.GetSize(); + for (uint32_t count = 0; count < size; ++count) { + SpdyStream2 *stream = + static_cast(self->mGoAwayStreamsToRestart.PopFront()); + + self->CloseStream(stream, NS_ERROR_NET_RESET); + if (stream->HasRegisteredID()) + self->mStreamIDHash.Remove(stream->StreamID()); + self->mStreamTransactionHash.Remove(stream->Transaction()); + } + + // Queued streams can also be deleted from this session and restarted + // in another one. (they were never sent on the network so they implicitly + // are not covered by the last-good id. + size = self->mQueuedStreams.GetSize(); + for (uint32_t count = 0; count < size; ++count) { + SpdyStream2 *stream = + static_cast(self->mQueuedStreams.PopFront()); + self->CloseStream(stream, NS_ERROR_NET_RESET); + self->mStreamTransactionHash.Remove(stream->Transaction()); + } + + LOG3(("SpdySession2::HandleGoAway %p GOAWAY Last-Good-ID 0x%X." + "live streams=%d\n", self, self->mGoAwayID, + self->mStreamTransactionHash.Count())); self->ResumeRecv(); self->ResetDownstreamState(); return NS_OK; diff --git a/netwerk/protocol/http/SpdySession2.h b/netwerk/protocol/http/SpdySession2.h index 0b8ab3aa8e5a..9cd073a35d6a 100644 --- a/netwerk/protocol/http/SpdySession2.h +++ b/netwerk/protocol/http/SpdySession2.h @@ -202,6 +202,10 @@ private: nsAutoPtr &, void *); + static PLDHashOperator GoAwayEnumerator(nsAHttpTransaction *, + nsAutoPtr &, + void *); + // This is intended to be nsHttpConnectionMgr:nsHttpConnectionHandle taken // from the first transaction on this session. That object contains the // pointer to the real network-level nsHttpConnection object. @@ -335,6 +339,9 @@ private: PRIntervalTime mLastDataReadEpoch; // used for IdleTime() PRIntervalTime mPingSentEpoch; uint32_t mNextPingID; + + // used as a temporary buffer while enumerating the stream hash during GoAway + nsDeque mGoAwayStreamsToRestart; }; }} // namespace mozilla::net diff --git a/netwerk/protocol/http/SpdySession3.cpp b/netwerk/protocol/http/SpdySession3.cpp index 1287ba12acde..5c6e82f9d36b 100644 --- a/netwerk/protocol/http/SpdySession3.cpp +++ b/netwerk/protocol/http/SpdySession3.cpp @@ -93,9 +93,12 @@ SpdySession3::ShutdownEnumerator(nsAHttpTransaction *key, // On a clean server hangup the server sets the GoAwayID to be the ID of // the last transaction it processed. If the ID of stream in the - // local session is greater than that it can safely be restarted because the - // server guarantees it was not partially processed. - if (self->mCleanShutdown && (stream->StreamID() > self->mGoAwayID)) + // local stream is greater than that it can safely be restarted because the + // server guarantees it was not partially processed. Streams that have not + // registered an ID haven't actually been sent yet so they can always be + // restarted. + if (self->mCleanShutdown && + (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID())) self->CloseStream(stream, NS_ERROR_NET_RESET); // can be restarted else self->CloseStream(stream, NS_ERROR_ABORT); @@ -103,6 +106,22 @@ SpdySession3::ShutdownEnumerator(nsAHttpTransaction *key, return PL_DHASH_NEXT; } +PLDHashOperator +SpdySession3::GoAwayEnumerator(nsAHttpTransaction *key, + nsAutoPtr &stream, + void *closure) +{ + SpdySession3 *self = static_cast(closure); + + // these streams were not processed by the server and can be restarted. + // Do that after the enumerator completes to avoid the risk of + // a restart event re-entrantly modifying this hash. + if (stream->StreamID() > self->mGoAwayID || !stream->HasRegisteredID()) + self->mGoAwayStreamsToRestart.Push(stream); + + return PL_DHASH_NEXT; +} + SpdySession3::~SpdySession3() { LOG3(("SpdySession3::~SpdySession3 %p mDownstreamState=%X", @@ -301,7 +320,6 @@ SpdySession3::AddStream(nsAHttpTransaction *aHttpTransaction, &mUpstreamZlib, aPriority); - LOG3(("SpdySession3::AddStream session=%p stream=%p NextID=0x%X (tentative)", this, stream, mNextStreamID)); @@ -1203,10 +1221,39 @@ SpdySession3::HandleGoAway(SpdySession3 *self) self->mGoAwayID = PR_ntohl(reinterpret_cast(self->mInputFrameBuffer.get())[2]); self->mCleanShutdown = true; - - LOG3(("SpdySession3::HandleGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X\n", - self, self->mGoAwayID, - PR_ntohl(reinterpret_cast(self->mInputFrameBuffer.get())[3]))); + + // Find streams greater than the last-good ID and mark them for deletion + // in the mGoAwayStreamsToRestart queue with the GoAwayEnumerator. They can + // be restarted. + self->mStreamTransactionHash.Enumerate(GoAwayEnumerator, self); + + // Process the streams marked for deletion and restart. + uint32_t size = self->mGoAwayStreamsToRestart.GetSize(); + for (uint32_t count = 0; count < size; ++count) { + SpdyStream3 *stream = + static_cast(self->mGoAwayStreamsToRestart.PopFront()); + + self->CloseStream(stream, NS_ERROR_NET_RESET); + if (stream->HasRegisteredID()) + self->mStreamIDHash.Remove(stream->StreamID()); + self->mStreamTransactionHash.Remove(stream->Transaction()); + } + + // Queued streams can also be deleted from this session and restarted + // in another one. (they were never sent on the network so they implicitly + // are not covered by the last-good id. + size = self->mQueuedStreams.GetSize(); + for (uint32_t count = 0; count < size; ++count) { + SpdyStream3 *stream = + static_cast(self->mQueuedStreams.PopFront()); + self->CloseStream(stream, NS_ERROR_NET_RESET); + self->mStreamTransactionHash.Remove(stream->Transaction()); + } + + LOG3(("SpdySession3::HandleGoAway %p GOAWAY Last-Good-ID 0x%X status 0x%X " + "live streams=%d\n", self, self->mGoAwayID, + PR_ntohl(reinterpret_cast(self->mInputFrameBuffer.get())[3]), + self->mStreamTransactionHash.Count())); self->ResumeRecv(); self->ResetDownstreamState(); diff --git a/netwerk/protocol/http/SpdySession3.h b/netwerk/protocol/http/SpdySession3.h index 64b3a36690c0..b3d196473af9 100644 --- a/netwerk/protocol/http/SpdySession3.h +++ b/netwerk/protocol/http/SpdySession3.h @@ -213,6 +213,10 @@ private: nsAutoPtr &, void *); + static PLDHashOperator GoAwayEnumerator(nsAHttpTransaction *, + nsAutoPtr &, + void *); + static PLDHashOperator UpdateServerRwinEnumerator(nsAHttpTransaction *, nsAutoPtr &, void *); @@ -346,6 +350,9 @@ private: PRIntervalTime mLastDataReadEpoch; // used for IdleTime() PRIntervalTime mPingSentEpoch; uint32_t mNextPingID; + + // used as a temporary buffer while enumerating the stream hash during GoAway + nsDeque mGoAwayStreamsToRestart; }; }} // namespace mozilla::net diff --git a/netwerk/protocol/http/SpdyStream2.h b/netwerk/protocol/http/SpdyStream2.h index a82a9f2d3ce8..61aa191329fa 100644 --- a/netwerk/protocol/http/SpdyStream2.h +++ b/netwerk/protocol/http/SpdyStream2.h @@ -41,6 +41,8 @@ public: mFullyOpen = 1; } + bool HasRegisteredID() { return mStreamID != 0; } + nsAHttpTransaction *Transaction() { return mTransaction; @@ -121,7 +123,7 @@ private: // The quanta upstream data frames are chopped into uint32_t mChunkSize; - // Flag is set when all http request headers have been read + // Flag is set when all http request headers have been read and ID is stable uint32_t mSynFrameComplete : 1; // Flag is set when the HTTP processor has more data to send diff --git a/netwerk/protocol/http/SpdyStream3.h b/netwerk/protocol/http/SpdyStream3.h index 5bbcc7f070d6..1397fd838b16 100644 --- a/netwerk/protocol/http/SpdyStream3.h +++ b/netwerk/protocol/http/SpdyStream3.h @@ -40,6 +40,8 @@ public: mFullyOpen = 1; } + bool HasRegisteredID() { return mStreamID != 0; } + nsAHttpTransaction *Transaction() { return mTransaction; @@ -143,7 +145,7 @@ private: // The quanta upstream data frames are chopped into uint32_t mChunkSize; - // Flag is set when all http request headers have been read + // Flag is set when all http request headers have been read and ID is stable uint32_t mSynFrameComplete : 1; // Flag is set when the HTTP processor has more data to send From a8f9e85501e110e7e432cd504a2a4f41ae019947 Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Thu, 20 Dec 2012 09:11:11 -0700 Subject: [PATCH 008/217] Bug 810347 - Provide option for setting remote test root; r=jmaher --- layout/tools/reftest/remotereftest.py | 15 ++++++++++----- layout/tools/reftest/runreftestb2g.py | 12 +++++------- testing/mochitest/runtestsb2g.py | 8 ++++---- testing/mochitest/runtestsremote.py | 13 +++++++++---- testing/xpcshell/remotexpcshelltests.py | 11 ++++++++--- testing/xpcshell/runtestsb2g.py | 10 ++++++---- 6 files changed, 42 insertions(+), 27 deletions(-) diff --git a/layout/tools/reftest/remotereftest.py b/layout/tools/reftest/remotereftest.py index 3885e515052c..e5a96eb93338 100644 --- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -24,7 +24,6 @@ class RemoteOptions(ReftestOptions): defaults = {} defaults["logFile"] = "reftest.log" # app, xrePath and utilityPath variables are set in main function - defaults["remoteTestRoot"] = None defaults["app"] = "" defaults["xrePath"] = "" defaults["utilityPath"] = "" @@ -87,13 +86,19 @@ class RemoteOptions(ReftestOptions): help = "the transport to use to communicate with device: [adb|sut]; default=sut") defaults["dm_trans"] = "sut" + self.add_option("--remoteTestRoot", action = "store", + type = "string", dest = "remoteTestRoot", + help = "remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)") + defaults["remoteTestRoot"] = None + defaults["localLogName"] = None self.set_defaults(**defaults) def verifyRemoteOptions(self, options): # Ensure our defaults are set properly for everything we can infer - options.remoteTestRoot = self._automation._devicemanager.getDeviceRoot() + '/reftest' + if not options.remoteTestRoot: + options.remoteTestRoot = self._automation._devicemanager.getDeviceRoot() + '/reftest' options.remoteProfile = options.remoteTestRoot + "/profile" # Verify that our remotewebserver is set properly @@ -377,11 +382,11 @@ def main(args): try: if (options.dm_trans == "adb"): if (options.deviceIP): - dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort) + dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) else: - dm = devicemanagerADB.DeviceManagerADB(None, None) + dm = devicemanagerADB.DeviceManagerADB(None, None, deviceRoot=options.remoteTestRoot) else: - dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort) + dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) except devicemanager.DMError: print "Error: exception while initializing devicemanager. Most likely the device is not in a testable state." return 1 diff --git a/layout/tools/reftest/runreftestb2g.py b/layout/tools/reftest/runreftestb2g.py index 1456cb82596c..ddc6b442c291 100644 --- a/layout/tools/reftest/runreftestb2g.py +++ b/layout/tools/reftest/runreftestb2g.py @@ -109,7 +109,7 @@ class B2GOptions(ReftestOptions): type="string", dest="logcat_dir", help="directory to store logcat dump files") defaults["logcat_dir"] = None - defaults["remoteTestRoot"] = None + defaults["remoteTestRoot"] = "/data/local/tests" defaults["logFile"] = "reftest.log" defaults["autorun"] = True defaults["closeWhenDone"] = True @@ -118,7 +118,8 @@ class B2GOptions(ReftestOptions): self.set_defaults(**defaults) def verifyRemoteOptions(self, options): - options.remoteTestRoot = self._automation._devicemanager.getDeviceRoot() + "/reftest" + if not options.remoteTestRoot: + options.remoteTestRoot = self._automation._devicemanager.getDeviceRoot() + "/reftest" options.remoteProfile = options.remoteTestRoot + "/profile" productRoot = options.remoteTestRoot + "/" + self._automation._product @@ -217,7 +218,6 @@ class B2GReftest(RefTest): self.remoteLogFile = options.remoteLogFile self.bundlesDir = '/system/b2g/distribution/bundles' self.userJS = '/data/local/user.js' - self.testDir = '/data/local/tests' self.remoteMozillaPath = '/data/b2g/mozilla' self.remoteProfilesIniPath = os.path.join(self.remoteMozillaPath, 'profiles.ini') self.originalProfilesIni = None @@ -495,7 +495,8 @@ def main(args=sys.argv[1:]): auto.marionette = marionette # create the DeviceManager - kwargs = {'adbPath': options.adbPath} + kwargs = {'adbPath': options.adbPath, + 'deviceRoot': options.remoteTestRoot} if options.deviceIP: kwargs.update({'host': options.deviceIP, 'port': options.devicePort}) @@ -522,9 +523,6 @@ def main(args=sys.argv[1:]): auto.logFinish = "REFTEST TEST-START | Shutdown" reftest = B2GReftest(auto, dm, options, SCRIPT_DIRECTORY) - # Create /data/local/tests, to force its use by DeviceManagerADB; - # B2G won't run correctly with the profile installed to /mnt/sdcard. - dm.mkDirs(reftest.testDir) logParent = os.path.dirname(options.remoteLogFile) dm.mkDir(logParent); diff --git a/testing/mochitest/runtestsb2g.py b/testing/mochitest/runtestsb2g.py index 3ab07f798b6d..93f3ace07f16 100644 --- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -103,7 +103,7 @@ class B2GOptions(MochitestOptions): help="directory to store logcat dump files") defaults["logcat_dir"] = None - defaults["remoteTestRoot"] = None + defaults["remoteTestRoot"] = "/data/local/tests" defaults["logFile"] = "mochitest.log" defaults["autorun"] = True defaults["closeWhenDone"] = True @@ -113,7 +113,8 @@ class B2GOptions(MochitestOptions): self.set_defaults(**defaults) def verifyRemoteOptions(self, options, automation): - options.remoteTestRoot = automation._devicemanager.getDeviceRoot() + if not options.remoteTestRoot: + options.remoteTestRoot = automation._devicemanager.getDeviceRoot() productRoot = options.remoteTestRoot + "/" + automation._product if options.utilityPath == self._automation.DIST_BIN: @@ -200,7 +201,6 @@ class B2GMochitest(Mochitest): _automation = None _dm = None localProfile = None - testDir = '/data/local/tests' def __init__(self, automation, devmgr, options): self._automation = automation @@ -498,7 +498,7 @@ def main(): # create the DeviceManager kwargs = {'adbPath': options.adbPath, - 'deviceRoot': B2GMochitest.testDir} + 'deviceRoot': options.remoteTestRoot} if options.deviceIP: kwargs.update({'host': options.deviceIP, 'port': options.devicePort}) diff --git a/testing/mochitest/runtestsremote.py b/testing/mochitest/runtestsremote.py index b3fa5b0cc952..32a499d77403 100644 --- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -91,7 +91,11 @@ class RemoteOptions(MochitestOptions): help = "name of the file containing the view ID map (fennec_ids.txt)") defaults["robocopIds"] = "" + self.add_option("--remoteTestRoot", action = "store", + type = "string", dest = "remoteTestRoot", + help = "remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)") defaults["remoteTestRoot"] = None + defaults["logFile"] = "mochitest.log" defaults["autorun"] = True defaults["closeWhenDone"] = True @@ -101,7 +105,8 @@ class RemoteOptions(MochitestOptions): self.set_defaults(**defaults) def verifyRemoteOptions(self, options, automation): - options.remoteTestRoot = automation._devicemanager.getDeviceRoot() + if not options.remoteTestRoot: + options.remoteTestRoot = automation._devicemanager.getDeviceRoot() productRoot = options.remoteTestRoot + "/" + automation._product if (options.utilityPath == self._automation.DIST_BIN): @@ -439,11 +444,11 @@ def main(): options, args = parser.parse_args() if (options.dm_trans == "adb"): if (options.deviceIP): - dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort) + dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) else: - dm = devicemanagerADB.DeviceManagerADB() + dm = devicemanagerADB.DeviceManagerADB(deviceRoot=options.remoteTestRoot) else: - dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort) + dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) auto.setDeviceManager(dm) options = parser.verifyRemoteOptions(options, auto) if (options == None): diff --git a/testing/xpcshell/remotexpcshelltests.py b/testing/xpcshell/remotexpcshelltests.py index f08cbf0c094e..42285fa15246 100644 --- a/testing/xpcshell/remotexpcshelltests.py +++ b/testing/xpcshell/remotexpcshelltests.py @@ -379,6 +379,11 @@ class RemoteXPCShellOptions(xpcshell.XPCShellOptions): help = "local path to bin directory") defaults["localBin"] = None + self.add_option("--remoteTestRoot", action = "store", + type = "string", dest = "remoteTestRoot", + help = "remote directory to use as test root (eg. /mnt/sdcard/tests or /data/local/tests)") + defaults["remoteTestRoot"] = None + self.set_defaults(**defaults) def verifyRemoteOptions(self, options): @@ -443,11 +448,11 @@ def main(): if (options.dm_trans == "adb"): if (options.deviceIP): - dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, packageName=None) + dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot) else: - dm = devicemanagerADB.DeviceManagerADB(packageName=None) + dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remoteTestRoot) else: - dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort) + dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot) if (options.deviceIP == None): print "Error: you must provide a device IP to connect to via the --device option" sys.exit(1) diff --git a/testing/xpcshell/runtestsb2g.py b/testing/xpcshell/runtestsb2g.py index 063f07f877ee..c7df0c97f300 100644 --- a/testing/xpcshell/runtestsb2g.py +++ b/testing/xpcshell/runtestsb2g.py @@ -24,13 +24,13 @@ class B2GXPCShellRemote(XPCShellRemote): if self.options.clean: # Ensure a fresh directory structure for our tests self.clean() - self.device.mkDir(DEVICE_TEST_ROOT) + self.device.mkDir(self.options.remoteTestRoot) XPCShellRemote.setupUtilities(self) def clean(self): print >>sys.stderr, "\nCleaning files from previous run.." - self.device.removeDir(DEVICE_TEST_ROOT) + self.device.removeDir(self.options.remoteTestRoot) # Overriden def setupTestDir(self): @@ -139,6 +139,7 @@ class B2GOptions(RemoteXPCShellOptions): help="Path to busybox binary to install on device") defaults['busybox'] = None + defaults["remoteTestRoot"] = DEVICE_TEST_ROOT defaults['dm_trans'] = 'adb' defaults['debugger'] = None defaults['debuggerArgs'] = None @@ -189,10 +190,11 @@ def main(): if options.deviceIP: kwargs['host'] = options.deviceIP kwargs['port'] = options.devicePort - kwargs['deviceRoot'] = DEVICE_TEST_ROOT + kwargs['deviceRoot'] = options.remoteTestRoot dm = devicemanagerADB.DeviceManagerADB(**kwargs) - options.remoteTestRoot = dm.getDeviceRoot() + if not options.remoteTestRoot: + options.remoteTestRoot = dm.getDeviceRoot() xpcsh = B2GXPCShellRemote(dm, options, args) try: From e7058559186089c87186fcfe8f6cc1f126289a2c Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Thu, 20 Dec 2012 11:06:42 -0800 Subject: [PATCH 009/217] Bug 814157 - Need additional security checks for the "desktop-notification" permission. r=fabrice --- b2g/chrome/content/shell.js | 12 +++++++++--- dom/ipc/ContentParent.cpp | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index b989b707fe62..db959efe3968 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -741,10 +741,16 @@ var AlertsHelper = { uid, name, null); }, - receiveMessage: function alert_receiveMessage(message) { - let data = message.data; + receiveMessage: function alert_receiveMessage(aMessage) { + if (!aMessage.target.assertPermission("desktop-notification")) { + Cu.reportError("Desktop-notification message " + aMessage.name + + " from a content process with no desktop-notification privileges."); + return null; + } + + let data = aMessage.data; let listener = { - mm: message.target, + mm: aMessage.target, title: data.title, text: data.text, manifestURL: data.manifestURL, diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6e364125a47f..88f0db741368 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1810,6 +1810,9 @@ ContentParent::RecvShowAlertNotification(const nsString& aImageUrl, const nsStri const nsString& aText, const bool& aTextClickable, const nsString& aCookie, const nsString& aName) { + if (!AssertAppProcessPermission(this, "desktop-notification")) { + return false; + } nsCOMPtr sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID)); if (sysAlerts) { sysAlerts->ShowAlertNotification(aImageUrl, aTitle, aText, aTextClickable, From db1a97de4377d7b07a75b2d1cd717fe9649236bd Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Thu, 20 Dec 2012 14:23:55 -0500 Subject: [PATCH 010/217] Bug 822956: backout 3dead2094684 due to breaking normal PeerConnection cases rs=me --- content/media/nsDOMMediaStream.cpp | 6 ++---- content/media/nsDOMMediaStream.h | 4 ++-- dom/media/MediaManager.cpp | 15 +++------------ dom/media/MediaManager.h | 9 ++------- 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/content/media/nsDOMMediaStream.cpp b/content/media/nsDOMMediaStream.cpp index 5943cc6ba654..c18433cec2fa 100644 --- a/content/media/nsDOMMediaStream.cpp +++ b/content/media/nsDOMMediaStream.cpp @@ -95,20 +95,18 @@ nsDOMLocalMediaStream::CreateSourceStream(uint32_t aHintContents) } already_AddRefed -nsDOMMediaStream::CreateTrackUnionStream(uint32_t aHintContents) +nsDOMMediaStream::CreateTrackUnionStream() { nsRefPtr stream = new nsDOMMediaStream(); - stream->SetHintContents(aHintContents); MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); stream->mStream = gm->CreateTrackUnionStream(stream); return stream.forget(); } already_AddRefed -nsDOMLocalMediaStream::CreateTrackUnionStream(uint32_t aHintContents) +nsDOMLocalMediaStream::CreateTrackUnionStream() { nsRefPtr stream = new nsDOMLocalMediaStream(); - stream->SetHintContents(aHintContents); MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); stream->mStream = gm->CreateTrackUnionStream(stream); return stream.forget(); diff --git a/content/media/nsDOMMediaStream.h b/content/media/nsDOMMediaStream.h index b108788d53ac..9c149a027671 100644 --- a/content/media/nsDOMMediaStream.h +++ b/content/media/nsDOMMediaStream.h @@ -70,7 +70,7 @@ public: /** * Create an nsDOMMediaStream whose underlying stream is a TrackUnionStream. */ - static already_AddRefed CreateTrackUnionStream(uint32_t aHintContents = 0); + static already_AddRefed CreateTrackUnionStream(); protected: // MediaStream is owned by the graph, but we tell it when to die, and it won't @@ -106,7 +106,7 @@ public: /** * Create an nsDOMLocalMediaStream whose underlying stream is a TrackUnionStream. */ - static already_AddRefed CreateTrackUnionStream(uint32_t aHintContents = 0); + static already_AddRefed CreateTrackUnionStream(); }; #endif /* NSDOMMEDIASTREAM_H_ */ diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 0e5706a3beab..1ddb606f6300 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -262,29 +262,21 @@ public: // Create a media stream. nsRefPtr stream; - nsRefPtr trackunion; uint32_t hints = (mAudioSource ? nsDOMMediaStream::HINT_CONTENTS_AUDIO : 0); hints |= (mVideoSource ? nsDOMMediaStream::HINT_CONTENTS_VIDEO : 0); - stream = nsDOMLocalMediaStream::CreateSourceStream(hints); - trackunion = nsDOMLocalMediaStream::CreateTrackUnionStream(hints); - if (!stream || !trackunion) { + stream = nsDOMLocalMediaStream::CreateSourceStream(hints); + if (!stream) { nsCOMPtr error(mError); LOG(("Returning error for getUserMedia() - no stream")); error->OnError(NS_LITERAL_STRING("NO_STREAM")); return NS_OK; } - // connect the source stream to the track union stream to avoid us blocking - trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true); - nsRefPtr port = trackunion->GetStream()->AsProcessedStream()-> - AllocateInputPort(stream->GetStream()->AsSourceStream(), - MediaInputPort::FLAG_BLOCK_OUTPUT); nsPIDOMWindow *window = static_cast (nsGlobalWindow::GetInnerWindowWithId(mWindowID)); if (window && window->GetExtantDoc()) { stream->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal()); - trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal()); } // Ensure there's a thread for gum to proxy to off main thread @@ -295,7 +287,6 @@ public: // when the page is invalidated (on navigation or close). GetUserMediaCallbackMediaStreamListener* listener = new GetUserMediaCallbackMediaStreamListener(mediaThread, stream, - port.forget(), mAudioSource, mVideoSource); stream->GetStream()->AddListener(listener); @@ -320,7 +311,7 @@ public: // This is safe since we're on main-thread, and the windowlist can only // be invalidated from the main-thread (see OnNavigation) LOG(("Returning success for getUserMedia()")); - success->OnSuccess(static_cast(trackunion)); + success->OnSuccess(static_cast(stream)); return NS_OK; } diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index 9b82396db411..7e1a54a912e0 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -89,7 +89,6 @@ public: , mStream(aStream) {} - // so we can send Stop without AddRef()ing from the MSG thread MediaOperationRunnable(MediaOperation aType, SourceMediaStream* aStream, MediaEngineSource* aAudioSource, @@ -162,7 +161,6 @@ public: } // Do this after stopping all tracks with EndTrack() mSourceStream->Finish(); - // the TrackUnion destination of the port will autofinish nsRefPtr event = new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STOPPING); @@ -192,14 +190,12 @@ class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener public: GetUserMediaCallbackMediaStreamListener(nsIThread *aThread, nsDOMMediaStream* aStream, - already_AddRefed aPort, MediaEngineSource* aAudioSource, MediaEngineSource* aVideoSource) : mMediaThread(aThread) , mAudioSource(aAudioSource) , mVideoSource(aVideoSource) - , mStream(aStream) - , mPort(aPort) {} + , mStream(aStream) {} void Invalidate() @@ -210,7 +206,7 @@ public: // thread. // XXX FIX! I'm cheating and passing a raw pointer to the sourcestream // which is valid as long as the mStream pointer here is. Need a better solution. - runnable = new MediaOperationRunnable(MEDIA_STOP, + runnable = new MediaOperationRunnable(MEDIA_STOP, mStream->GetStream()->AsSourceStream(), mAudioSource, mVideoSource); mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL); @@ -243,7 +239,6 @@ private: nsRefPtr mAudioSource; nsRefPtr mVideoSource; nsRefPtr mStream; - nsRefPtr mPort; }; typedef nsTArray > StreamListeners; From 8547c5738638420f6db5ba2e1cf41c1f64d1e9a9 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 20 Dec 2012 11:26:35 -0800 Subject: [PATCH 011/217] Bug 822961 - Implement JS_WrapId. r=billm --- js/src/jsapi.cpp | 8 ++++++++ js/src/jsapi.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 555df85f2480..9d4aa5d5dfcb 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1536,6 +1536,14 @@ JS_WrapValue(JSContext *cx, jsval *vp) return cx->compartment->wrap(cx, vp); } +JS_PUBLIC_API(JSBool) +JS_WrapId(JSContext *cx, jsid *idp) +{ + AssertHeapIsIdle(cx); + CHECK_REQUEST(cx); + return cx->compartment->wrapId(cx, idp); +} + /* * Identity remapping. Not for casual consumers. * diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 3be2b0768d1a..7156e99b1b98 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3241,6 +3241,9 @@ JS_WrapObject(JSContext *cx, JSObject **objp); extern JS_PUBLIC_API(JSBool) JS_WrapValue(JSContext *cx, jsval *vp); +extern JS_PUBLIC_API(JSBool) +JS_WrapId(JSContext *cx, jsid *idp); + extern JS_PUBLIC_API(JSObject *) JS_TransplantObject(JSContext *cx, JSObject *origobj, JSObject *target); From efd3605ec9fda96c85c5076b0e1a17f2185e5186 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 20 Dec 2012 11:26:35 -0800 Subject: [PATCH 012/217] Bug 822383 - Allow arbitrary proxies on the scope chain. r=luke --- js/src/js.msg | 2 +- js/src/jsinterp.cpp | 14 ++++++++------ js/src/jsinterpinlines.h | 1 - 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/js/src/js.msg b/js/src/js.msg index befda7d04182..71fc75458736 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -316,7 +316,7 @@ MSG_DEF(JSMSG_SC_RECURSION, 262, 0, JSEXN_INTERNALERR, "recursive obje MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT, 263, 0, JSEXN_TYPEERR, "can't wrap XML objects") MSG_DEF(JSMSG_BAD_CLONE_VERSION, 264, 0, JSEXN_ERR, "unsupported structured clone version") MSG_DEF(JSMSG_CANT_CLONE_OBJECT, 265, 0, JSEXN_TYPEERR, "can't clone object") -MSG_DEF(JSMSG_NON_NATIVE_SCOPE, 266, 0, JSEXN_TYPEERR, "non-native scope object") +MSG_DEF(JSMSG_UNUSED266, 266, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function") MSG_DEF(JSMSG_INVALID_FOR_IN_INIT, 268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer") MSG_DEF(JSMSG_CLEARED_SCOPE, 269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope") diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 3d7ffd2f7cf7..c5c259a94ec0 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -552,12 +552,14 @@ js::Execute(JSContext *cx, HandleScript script, JSObject &scopeChainArg, Value * if (!scopeChain) return false; - /* If we were handed a non-native object, complain bitterly. */ - if (!scopeChain->isNative()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NON_NATIVE_SCOPE); - return false; - } - JS_ASSERT(!scopeChain->getOps()->defineProperty); + /* Ensure the scope chain is all same-compartment and terminates in a global. */ +#ifdef DEBUG + RawObject s = scopeChain; + do { + assertSameCompartment(cx, s); + JS_ASSERT_IF(!s->enclosingScope(), s->isGlobal()); + } while ((s = s->enclosingScope())); +#endif /* The VAROBJFIX option makes varObj == globalObj in global code. */ if (!cx->hasRunOption(JSOPTION_VAROBJFIX)) { diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 2699a0498f10..87df5aefc035 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -476,7 +476,6 @@ inline bool DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs) { JS_ASSERT(varobj->isVarObj()); - JS_ASSERT(!varobj->getOps()->defineProperty || varobj->isDebugScope()); RootedShape prop(cx); RootedObject obj2(cx); From acc1a59a1a90e8e572120e2e389d8ccbfb1da752 Mon Sep 17 00:00:00 2001 From: Sriram Raghuraman Date: Mon, 17 Dec 2012 23:00:01 +0530 Subject: [PATCH 013/217] Bug 715736 - Add 'Save as Wallpaper' Android functionality in context menu when viewing images. r=snorp,margaret --- mobile/android/base/AlertNotification.java | 4 + mobile/android/base/AndroidManifest.xml.in | 1 + mobile/android/base/GeckoApp.java | 170 +++++++++++++++++- .../base/locales/en-US/android_strings.dtd | 3 + mobile/android/base/strings.xml.in | 5 + mobile/android/chrome/content/browser.js | 12 ++ .../locales/en-US/chrome/browser.properties | 1 + 7 files changed, 195 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/AlertNotification.java b/mobile/android/base/AlertNotification.java index 08325bc61a65..bed77c650b94 100644 --- a/mobile/android/base/AlertNotification.java +++ b/mobile/android/base/AlertNotification.java @@ -55,6 +55,10 @@ public class AlertNotification mNotificationManager.notify(mId, this); } + public void cancel() { + mNotificationManager.cancel(mId); + } + public void setCustomIcon(Uri aIconUri) { if (aIconUri == null || aIconUri.getScheme() == null) return; diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in index 3fb0308f616e..170f8ac0583d 100644 --- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -26,6 +26,7 @@ + diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index cd72fee63b6c..2b031e6f0e18 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -28,6 +28,9 @@ import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.app.Notification; +import android.app.PendingIntent; +import android.app.WallpaperManager; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; @@ -43,7 +46,9 @@ import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Color; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.hardware.Sensor; @@ -61,8 +66,10 @@ import android.os.StrictMode; import android.os.SystemClock; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Base64; import android.util.Log; import android.util.SparseBooleanArray; +import android.view.Display; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -70,6 +77,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; @@ -94,6 +102,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.HttpURLConnection; @@ -1016,6 +1025,9 @@ abstract public class GeckoApp String src = message.getString("url"); String type = message.getString("mime"); GeckoAppShell.shareImage(src, type); + } else if (event.equals("Wallpaper:Set")) { + String src = message.getString("url"); + setImageAsWallpaper(src); } else if (event.equals("Sanitize:ClearHistory")) { handleClearHistory(); } else if (event.equals("Update:Check")) { @@ -1311,7 +1323,161 @@ abstract public class GeckoApp } }); } - + + private void setImageAsWallpaper(final String aSrc) { + final String progText = mAppContext.getString(R.string.wallpaper_progress); + final String successText = mAppContext.getString(R.string.wallpaper_success); + final String failureText = mAppContext.getString(R.string.wallpaper_fail); + final String fileName = aSrc.substring(aSrc.lastIndexOf("/") + 1); + final PendingIntent emptyIntent = PendingIntent.getActivity(mAppContext, 0, new Intent(), 0); + final AlertNotification notification = new AlertNotification(mAppContext, fileName.hashCode(), + R.drawable.alert_download, fileName, progText, System.currentTimeMillis() ); + notification.setLatestEventInfo(mAppContext, fileName, progText, emptyIntent ); + notification.flags |= Notification.FLAG_ONGOING_EVENT; + notification.show(); + new GeckoAsyncTask(mAppContext, GeckoAppShell.getHandler()){ + + @Override + protected Boolean doInBackground(Void... params) { + WallpaperManager mgr = WallpaperManager.getInstance(mAppContext); + + // Determine the ideal width and height of the wallpaper + // for the device + + int idealWidth = mgr.getDesiredMinimumWidth(); + int idealHeight = mgr.getDesiredMinimumHeight(); + + // Sometimes WallpaperManager's getDesiredMinimum*() methods + // can return 0 if a Remote Exception occurs when calling the + // Wallpaper Service. So if that fails, we are calculating + // the ideal width and height from the device's display + // resolution (excluding the decorated area) + + if(idealWidth <= 0 || idealHeight <= 0) { + int orientation; + Display defaultDisplay = getWindowManager().getDefaultDisplay(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + orientation = defaultDisplay.getRotation(); + } else { + orientation = defaultDisplay.getOrientation(); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + Point size = new Point(); + defaultDisplay.getSize(size); + // The ideal wallpaper width is always twice the size of + // display width + if (orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_270) { + idealWidth = size.x * 2; + idealHeight = size.y; + } else { + idealWidth = size.y; + idealHeight = size.x * 2; + } + } else { + if (orientation == Surface.ROTATION_0 || orientation == Surface.ROTATION_270) { + idealWidth = defaultDisplay.getWidth() * 2; + idealHeight = defaultDisplay.getHeight(); + } else { + idealWidth = defaultDisplay.getHeight(); + idealHeight = defaultDisplay.getWidth() * 2; + } + } + } + + boolean isDataURI = aSrc.startsWith("data:"); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + Bitmap image = null; + InputStream is = null; + ByteArrayOutputStream os = null; + try{ + if (isDataURI) { + int dataStart = aSrc.indexOf(','); + byte[] buf = Base64.decode(aSrc.substring(dataStart+1), Base64.DEFAULT); + BitmapFactory.decodeByteArray(buf, 0, buf.length, options); + options.inSampleSize = getBitmapSampleSize(options, idealWidth, idealHeight); + options.inJustDecodeBounds = false; + image = BitmapFactory.decodeByteArray(buf, 0, buf.length, options); + } else { + int byteRead; + byte[] buf = new byte[4192]; + os = new ByteArrayOutputStream(); + URL url = new URL(aSrc); + is = url.openStream(); + + // Cannot read from same stream twice. Also, InputStream from + // URL does not support reset. So converting to byte array + + while((byteRead = is.read(buf)) != -1) { + os.write(buf, 0, byteRead); + } + byte[] imgBuffer = os.toByteArray(); + BitmapFactory.decodeByteArray(imgBuffer, 0, imgBuffer.length, options); + options.inSampleSize = getBitmapSampleSize(options, idealWidth, idealHeight); + options.inJustDecodeBounds = false; + image = BitmapFactory.decodeByteArray(imgBuffer, 0, imgBuffer.length, options); + } + if(image != null) { + mgr.setBitmap(image); + return true; + } else { + return false; + } + } catch(OutOfMemoryError ome) { + Log.e(LOGTAG, "Out of Memmory when coverting to byte array", ome); + return false; + } catch(IOException ioe) { + Log.e(LOGTAG, "I/O Exception while setting wallpaper", ioe); + return false; + } finally { + if(is != null) { + try { + is.close(); + } catch(IOException ioe) { + Log.w(LOGTAG, "I/O Exception while closing stream", ioe); + } + } + if(os != null) { + try { + os.close(); + } catch(IOException ioe) { + Log.w(LOGTAG, "I/O Exception while closing stream", ioe); + } + } + } + } + + @Override + protected void onPostExecute(Boolean success) { + notification.cancel(); + notification.flags = 0; + notification.flags |= Notification.FLAG_AUTO_CANCEL; + if(!success) { + notification.tickerText = failureText; + notification.setLatestEventInfo(mAppContext, fileName, failureText, emptyIntent); + } else { + notification.tickerText = successText; + notification.setLatestEventInfo(mAppContext, fileName, successText, emptyIntent); + } + notification.show(); + } + }.execute(); + } + + private int getBitmapSampleSize(BitmapFactory.Options options, int idealWidth, int idealHeight) { + int width = options.outWidth; + int height = options.outHeight; + int inSampleSize = 1; + if (height > idealHeight || width > idealWidth) { + if (width > height) { + inSampleSize = Math.round((float)height / (float)idealHeight); + } else { + inSampleSize = Math.round((float)width / (float)idealWidth); + } + } + return inSampleSize; + } + private void hidePluginLayer(Layer layer) { LayerView layerView = mLayerView; layerView.removeLayer(layer); @@ -1707,6 +1873,7 @@ abstract public class GeckoApp registerEventListener("DesktopMode:Changed"); registerEventListener("Share:Text"); registerEventListener("Share:Image"); + registerEventListener("Wallpaper:Set"); registerEventListener("Sanitize:ClearHistory"); registerEventListener("Update:Check"); registerEventListener("PrivateBrowsing:Data"); @@ -2171,6 +2338,7 @@ abstract public class GeckoApp unregisterEventListener("DesktopMode:Changed"); unregisterEventListener("Share:Text"); unregisterEventListener("Share:Image"); + unregisterEventListener("Wallpaper:Set"); unregisterEventListener("Sanitize:ClearHistory"); unregisterEventListener("Update:Check"); unregisterEventListener("PrivateBrowsing:Data"); diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index a14de61497e2..91e66e4d37c7 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -139,6 +139,9 @@ size. --> + + + diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index 920e94badf37..865052c956f4 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -255,4 +255,9 @@ &suggestions_prompt2; + + &wallpaper_success; + &wallpaper_progress; + &wallpaper_fail; + diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 9d09a3ca04a6..4c47075ec348 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -483,6 +483,18 @@ var BrowserApp = { ContentAreaUtils.internalSave(aTarget.currentURI.spec, null, null, contentDisposition, type, false, "SaveImageTitle", null, aTarget.ownerDocument.documentURIObject, aTarget.ownerDocument, true, null); }); + + NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.setWallpaper"), + NativeWindow.contextmenus.imageSaveableContext, + function(aTarget) { + let src = aTarget.src; + sendMessageToJava({ + gecko: { + type: "Wallpaper:Set", + url: src + } + }); + }); }, onAppUpdated: function() { diff --git a/mobile/android/locales/en-US/chrome/browser.properties b/mobile/android/locales/en-US/chrome/browser.properties index 3bb60e2cf593..c9c1b2b5e7a1 100644 --- a/mobile/android/locales/en-US/chrome/browser.properties +++ b/mobile/android/locales/en-US/chrome/browser.properties @@ -229,6 +229,7 @@ contextmenu.fullScreen=Full Screen contextmenu.copyImageLocation=Copy Image Location contextmenu.shareImage=Share Image contextmenu.saveImage=Save Image +contextmenu.setWallpaper=Set as Wallpaper contextmenu.addSearchEngine=Add Search Engine contextmenu.playMedia=Play contextmenu.pauseMedia=Pause From 477ae94921af3a8ff9f3345fa19b21e14b9b91e7 Mon Sep 17 00:00:00 2001 From: Andrew Halberstadt Date: Thu, 20 Dec 2012 10:24:25 -0500 Subject: [PATCH 014/217] Bug 821865 - Update mozdevice to version 0.18, r=jhammel --- .../mozdevice/mozdevice/devicemanager.py | 4 +- .../mozdevice/mozdevice/devicemanagerADB.py | 65 ++++++++++--------- .../mozdevice/mozdevice/devicemanagerSUT.py | 30 +++++---- testing/mozbase/mozdevice/setup.py | 12 +--- testing/mozbase/mozdevice/tests/sut_mkdir.py | 6 +- testing/mozbase/mozdevice/tests/sut_push.py | 22 ++----- 6 files changed, 67 insertions(+), 72 deletions(-) diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanager.py b/testing/mozbase/mozdevice/mozdevice/devicemanager.py index 9cb6616421a5..3d3e7a171b94 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanager.py @@ -66,7 +66,7 @@ class DeviceManager: return output @abstractmethod - def pushFile(self, localname, destname): + def pushFile(self, localname, destname, retryLimit=1): """ Copies localname from the host to destname on the device """ @@ -94,7 +94,7 @@ class DeviceManager: self.mkDir(name) # mkDir will check previous existence @abstractmethod - def pushDir(self, localDir, remoteDir): + def pushDir(self, localDir, remoteDir, retryLimit=1): """ Push localDir from host to remoteDir on the device """ diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py index 9d6fbbeec9ce..c9cfa302f395 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py @@ -23,11 +23,11 @@ class DeviceManagerADB(DeviceManager): _tempDir = None default_timeout = 300 - def __init__(self, host=None, port=20701, retrylimit=5, packageName='fennec', + def __init__(self, host=None, port=20701, retryLimit=5, packageName='fennec', adbPath='adb', deviceSerial=None, deviceRoot=None, **kwargs): self.host = host self.port = port - self.retrylimit = retrylimit + self.retryLimit = retryLimit self.deviceRoot = deviceRoot # the path to adb, or 'adb' to assume that it's on the PATH @@ -164,7 +164,7 @@ class DeviceManagerADB(DeviceManager): def _disconnectRemoteADB(self): self._checkCmd(["disconnect", self.host + ":" + str(self.port)]) - def pushFile(self, localname, destname): + def pushFile(self, localname, destname, retryLimit=None): """ Copies localname from the host to destname on the device """ @@ -172,20 +172,23 @@ class DeviceManagerADB(DeviceManager): # but that would be different behaviour from devicemanagerSUT. Throw # an exception so we have the same behaviour between the two # implementations + retryLimit = retryLimit or self.retryLimit if self.dirExists(destname): raise DMError("Attempted to push a file (%s) to a directory (%s)!" % (localname, destname)) if self._useRunAs: remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname) - self._checkCmd(["push", os.path.realpath(localname), remoteTmpFile]) + self._checkCmd(["push", os.path.realpath(localname), remoteTmpFile], + retryLimit=retryLimit) if self._useDDCopy: self.shellCheckOutput(["dd", "if=" + remoteTmpFile, "of=" + destname]) else: self.shellCheckOutput(["cp", remoteTmpFile, destname]) self.shellCheckOutput(["rm", remoteTmpFile]) else: - self._checkCmd(["push", os.path.realpath(localname), destname]) + self._checkCmd(["push", os.path.realpath(localname), destname], + retryLimit=retryLimit) def mkDir(self, name): """ @@ -195,7 +198,7 @@ class DeviceManagerADB(DeviceManager): if 'read-only file system' in result.lower(): raise DMError("Error creating directory: read only file system") - def pushDir(self, localDir, remoteDir): + def pushDir(self, localDir, remoteDir, retryLimit=None): """ Push localDir from host to remoteDir on the device """ @@ -203,6 +206,7 @@ class DeviceManagerADB(DeviceManager): # contains symbolic links, the links are pushed, rather than the linked # files; we either zip/unzip or re-copy the directory into a temporary # one to get around this limitation + retryLimit = retryLimit or self.retryLimit if not self.dirExists(remoteDir): self.mkDirs(remoteDir+"/x") if self._useZip: @@ -211,22 +215,23 @@ class DeviceManagerADB(DeviceManager): remoteZip = remoteDir + "/adbdmtmp.zip" subprocess.Popen(["zip", "-r", localZip, '.'], cwd=localDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() - self.pushFile(localZip, remoteZip) + self.pushFile(localZip, remoteZip, retryLimit=retryLimit) os.remove(localZip) - data = self._runCmdAs(["shell", "unzip", "-o", remoteZip, "-d", remoteDir]).stdout.read() - self._checkCmdAs(["shell", "rm", remoteZip]) + data = self._runCmdAs(["shell", "unzip", "-o", remoteZip, + "-d", remoteDir]).stdout.read() + self._checkCmdAs(["shell", "rm", remoteZip], retryLimit=retryLimit) if re.search("unzip: exiting", data) or re.search("Operation not permitted", data): raise Exception("unzip failed, or permissions error") except: print "zip/unzip failure: falling back to normal push" self._useZip = False - self.pushDir(localDir, remoteDir) + self.pushDir(localDir, remoteDir, retryLimit=retryLimit) else: tmpDir = tempfile.mkdtemp() # copytree's target dir must not already exist, so create a subdir tmpDirTarget = os.path.join(tmpDir, "tmp") shutil.copytree(localDir, tmpDirTarget) - self._checkCmd(["push", tmpDirTarget, remoteDir]) + self._checkCmd(["push", tmpDirTarget, remoteDir], retryLimit=retryLimit) shutil.rmtree(tmpDir) def dirExists(self, remotePath): @@ -260,18 +265,12 @@ class DeviceManagerADB(DeviceManager): if self.fileExists(filename): self._runCmd(["shell", "rm", filename]) - def _removeSingleDir(self, remoteDir): - """ - Deletes a single empty directory - """ - return self._runCmd(["shell", "rmdir", remoteDir]).stdout.read() - def removeDir(self, remoteDir): """ Does a recursive delete of directory on the device: rm -Rf remoteDir """ if (self.dirExists(remoteDir)): - self._runCmd(["shell", "rm", "-r", remoteDir]) + self._runCmd(["shell", "rm", "-r", remoteDir]).wait() else: self.removeFile(remoteDir.strip()) @@ -698,13 +697,14 @@ class DeviceManagerADB(DeviceManager): # timeout is specified in seconds, and if no timeout is given, # we will run until we hit the default_timeout specified in the __init__ - def _checkCmd(self, args, timeout=None): + def _checkCmd(self, args, timeout=None, retryLimit=None): """ Runs a command using adb and waits for the command to finish. If timeout is specified, the process is killed after seconds. returns: returncode from subprocess.Popen """ + retryLimit = retryLimit or self.retryLimit # use run-as to execute commands as the package we're testing if # possible finalArgs = [self._adbPath] @@ -720,18 +720,22 @@ class DeviceManagerADB(DeviceManager): timeout = self.default_timeout timeout = int(timeout) - proc = subprocess.Popen(finalArgs) - start_time = time.time() - ret_code = proc.poll() - while ((time.time() - start_time) <= timeout) and ret_code == None: - time.sleep(self._pollingInterval) + retries = 0 + while retries < retryLimit: + proc = subprocess.Popen(finalArgs) + start_time = time.time() ret_code = proc.poll() - if ret_code == None: - proc.kill() - raise DMError("Timeout exceeded for _checkCmd call") - return ret_code + while ((time.time() - start_time) <= timeout) and ret_code == None: + time.sleep(self._pollingInterval) + ret_code = proc.poll() + if ret_code == None: + proc.kill() + retries += 1 + continue + return ret_code + raise DMError("Timeout exceeded for _checkCmd call after %d retries." % retries) - def _checkCmdAs(self, args, timeout=None): + def _checkCmdAs(self, args, timeout=None, retryLimit=None): """ Runs a command using adb and waits for command to finish If self._useRunAs is True, the command is run-as user specified in self._packageName @@ -739,10 +743,11 @@ class DeviceManagerADB(DeviceManager): returns: returncode from subprocess.Popen """ + retryLimit = retryLimit or self.retryLimit if (self._useRunAs): args.insert(1, "run-as") args.insert(2, self._packageName) - return self._checkCmd(args, timeout) + return self._checkCmd(args, timeout, retryLimit=retryLimit) def chmodDir(self, remoteDir, mask="777"): """ diff --git a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py index 5ed198f6e5ec..18b16cb4ab48 100644 --- a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py +++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py @@ -25,10 +25,10 @@ class DeviceManagerSUT(DeviceManager): _agentErrorRE = re.compile('^##AGENT-WARNING##\ ?(.*)') default_timeout = 300 - def __init__(self, host, port = 20701, retrylimit = 5, deviceRoot = None, **kwargs): + def __init__(self, host, port = 20701, retryLimit = 5, deviceRoot = None, **kwargs): self.host = host self.port = port - self.retrylimit = retrylimit + self.retryLimit = retryLimit self._sock = None self._everConnected = False self.deviceRoot = deviceRoot @@ -99,9 +99,9 @@ class DeviceManagerSUT(DeviceManager): return True return False - def _sendCmds(self, cmdlist, outputfile, timeout = None): + def _sendCmds(self, cmdlist, outputfile, timeout = None, retryLimit = None): """ - Wrapper for _doCmds that loops up to self.retrylimit iterations + Wrapper for _doCmds that loops up to retryLimit iterations """ # this allows us to move the retry logic outside of the _doCmds() to make it # easier for debugging in the future. @@ -109,8 +109,9 @@ class DeviceManagerSUT(DeviceManager): # one fails. this is necessary in particular for pushFile(), where we don't want # to accidentally send extra data if a failure occurs during data transmission. + retryLimit = retryLimit or self.retryLimit retries = 0 - while retries < self.retrylimit: + while retries < retryLimit: try: self._doCmds(cmdlist, outputfile, timeout) return @@ -123,20 +124,21 @@ class DeviceManagerSUT(DeviceManager): print err retries += 1 # if we lost the connection or failed to establish one, wait a bit - if retries < self.retrylimit and not self._sock: + if retries < retryLimit and not self._sock: sleep_time = 5 * retries print 'Could not connect; sleeping for %d seconds.' % sleep_time time.sleep(sleep_time) - raise DMError("Remote Device Error: unable to connect to %s after %s attempts" % (self.host, self.retrylimit)) + raise DMError("Remote Device Error: unable to connect to %s after %s attempts" % (self.host, retryLimit)) - def _runCmds(self, cmdlist, timeout = None): + def _runCmds(self, cmdlist, timeout = None, retryLimit = None): """ Similar to _sendCmds, but just returns any output as a string instead of writing to a file """ + retryLimit = retryLimit or self.retryLimit outputfile = StringIO.StringIO() - self._sendCmds(cmdlist, outputfile, timeout) + self._sendCmds(cmdlist, outputfile, timeout, retryLimit=retryLimit) outputfile.seek(0) return outputfile.read() @@ -327,17 +329,18 @@ class DeviceManagerSUT(DeviceManager): # woops, we couldn't find an end of line/return value raise DMError("Automation Error: Error finding end of line/return value when running '%s'" % cmdline) - def pushFile(self, localname, destname): + def pushFile(self, localname, destname, retryLimit = None): """ Copies localname from the host to destname on the device """ + retryLimit = retryLimit or self.retryLimit self.mkDirs(destname) try: filesize = os.path.getsize(localname) with open(localname, 'rb') as f: remoteHash = self._runCmds([{ 'cmd': 'push ' + destname + ' ' + str(filesize), - 'data': f.read() }]).strip() + 'data': f.read() }], retryLimit=retryLimit).strip() except OSError: raise DMError("DeviceManager: Error reading file to push") @@ -357,10 +360,11 @@ class DeviceManagerSUT(DeviceManager): if not self.dirExists(name): self._runCmds([{ 'cmd': 'mkdr ' + name }]) - def pushDir(self, localDir, remoteDir): + def pushDir(self, localDir, remoteDir, retryLimit = None): """ Push localDir from host to remoteDir on the device """ + retryLimit = retryLimit or self.retryLimit if (self.debug >= 2): print "pushing directory: %s to %s" % (localDir, remoteDir) @@ -382,7 +386,7 @@ class DeviceManagerSUT(DeviceManager): self.mkDirs(remoteName) existentDirectories.append(parent) - self.pushFile(os.path.join(root, f), remoteName) + self.pushFile(os.path.join(root, f), remoteName, retryLimit=retryLimit) def dirExists(self, remotePath): diff --git a/testing/mozbase/mozdevice/setup.py b/testing/mozbase/mozdevice/setup.py index 5bed2086043d..261067a06470 100644 --- a/testing/mozbase/mozdevice/setup.py +++ b/testing/mozbase/mozdevice/setup.py @@ -2,24 +2,16 @@ # 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/. -import os from setuptools import setup -PACKAGE_VERSION = '0.17' - -# take description from README -here = os.path.dirname(os.path.abspath(__file__)) -try: - description = file(os.path.join(here, 'README.md')).read() -except (OSError, IOError): - description = '' +PACKAGE_VERSION = '0.18' deps = ['mozprocess == 0.8'] setup(name='mozdevice', version=PACKAGE_VERSION, description="Mozilla-authored device management", - long_description=description, + long_description="see http://mozbase.readthedocs.org/", classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers keywords='', author='Mozilla Automation and Testing Team', diff --git a/testing/mozbase/mozdevice/tests/sut_mkdir.py b/testing/mozbase/mozdevice/tests/sut_mkdir.py index 4a1c48b3c987..783485feb39e 100644 --- a/testing/mozbase/mozdevice/tests/sut_mkdir.py +++ b/testing/mozbase/mozdevice/tests/sut_mkdir.py @@ -5,7 +5,8 @@ import unittest class PushTest(unittest.TestCase): def test_mkdirs(self): - subTests = [ { 'cmds': [ ("isdir /mnt", "TRUE"), + subTests = [ { 'cmds': [ ("isdir /mnt/sdcard/baz/boop", "FALSE"), + ("isdir /mnt", "TRUE"), ("isdir /mnt/sdcard", "TRUE"), ("isdir /mnt/sdcard/baz", "FALSE"), ("mkdr /mnt/sdcard/baz", @@ -14,7 +15,8 @@ class PushTest(unittest.TestCase): ("mkdr /mnt/sdcard/baz/boop", "/mnt/sdcard/baz/boop successfully created") ], 'expectException': False }, - { 'cmds': [ ("isdir /mnt", "TRUE"), + { 'cmds': [ ("isdir /mnt/sdcard/baz/boop", "FALSE"), + ("isdir /mnt", "TRUE"), ("isdir /mnt/sdcard", "TRUE"), ("isdir /mnt/sdcard/baz", "FALSE"), ("mkdr /mnt/sdcard/baz", diff --git a/testing/mozbase/mozdevice/tests/sut_push.py b/testing/mozbase/mozdevice/tests/sut_push.py index 3a6d011c49d9..3d02a3a9f5a2 100644 --- a/testing/mozbase/mozdevice/tests/sut_push.py +++ b/testing/mozbase/mozdevice/tests/sut_push.py @@ -16,8 +16,7 @@ class PushTest(unittest.TestCase): # (good response, no exception), (bad response, exception) for response in [ (expectedResponse, False), ("BADHASH", True) ]: cmd = "push /mnt/sdcard/foobar %s" % len(pushfile) - a = MockAgent(self, commands = [("isdir /mnt", "TRUE"), - ("isdir /mnt/sdcard", "TRUE"), + a = MockAgent(self, commands = [("isdir /mnt/sdcard", "TRUE"), (cmd, response[0])]) exceptionThrown = False with tempfile.NamedTemporaryFile() as f: @@ -44,27 +43,20 @@ class PushTest(unittest.TestCase): f.write(pushfile) f.flush() - subTests = [ { 'cmds': [ ("isdir /mnt", "TRUE"), - ("isdir /mnt/sdcard", "TRUE"), - ("isdir /mnt/sdcard/baz", "TRUE"), - ("isdir /mnt", "TRUE"), - ("isdir /mnt/sdcard", "TRUE"), - ("isdir /mnt/sdcard/baz", "TRUE"), + subTests = [ { 'cmds': [ ("isdir /mnt/sdcard//baz", "TRUE"), + ("isdir /mnt/sdcard//baz", "TRUE"), ("push /mnt/sdcard//baz/%s %s" % (os.path.basename(f.name), len(pushfile)), expectedFileResponse) ], 'expectException': False }, - { 'cmds': [ ("isdir /mnt", "TRUE"), - ("isdir /mnt/sdcard", "TRUE"), - ("isdir /mnt/sdcard/baz", "TRUE"), - ("isdir /mnt", "TRUE"), - ("isdir /mnt/sdcard", "TRUE"), - ("isdir /mnt/sdcard/baz", "TRUE"), + { 'cmds': [ ("isdir /mnt/sdcard//baz", "TRUE"), + ("isdir /mnt/sdcard//baz", "TRUE"), ("push /mnt/sdcard//baz/%s %s" % (os.path.basename(f.name), len(pushfile)), "BADHASH") ], 'expectException': True }, - { 'cmds': [ ("isdir /mnt", "FALSE"), + { 'cmds': [ ("isdir /mnt/sdcard//baz", "FALSE"), + ("isdir /mnt", "FALSE"), ("mkdr /mnt", "##AGENT-WARNING## Could not create the directory /mnt") ], 'expectException': True }, From 694274b38b1677845f53f11f53874fb2e9a96703 Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Thu, 20 Dec 2012 11:42:17 -0800 Subject: [PATCH 015/217] Bug 822816 - Fix double tap activation in jellybean. r=davidb --- accessible/src/jsat/TouchAdapter.jsm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/accessible/src/jsat/TouchAdapter.jsm b/accessible/src/jsat/TouchAdapter.jsm index 765baa0ca3bb..d8602e8067dd 100644 --- a/accessible/src/jsat/TouchAdapter.jsm +++ b/accessible/src/jsat/TouchAdapter.jsm @@ -69,9 +69,6 @@ this.TouchAdapter = { target.addEventListener('mousemove', this, true, true); target.addEventListener('mouseenter', this, true, true); target.addEventListener('mouseleave', this, true, true); - target.addEventListener('mousedown', this, true, true); - target.addEventListener('mouseup', this, true, true); - target.addEventListener('click', this, true, true); target.addEventListener('touchend', this, true, true); target.addEventListener('touchmove', this, true, true); @@ -97,9 +94,6 @@ this.TouchAdapter = { target.removeEventListener('mousemove', this, true, true); target.removeEventListener('mouseenter', this, true, true); target.removeEventListener('mouseleave', this, true, true); - target.removeEventListener('mousedown', this, true, true); - target.removeEventListener('mouseup', this, true, true); - target.removeEventListener('click', this, true, true); target.removeEventListener('touchend', this, true, true); target.removeEventListener('touchmove', this, true, true); @@ -128,7 +122,9 @@ this.TouchAdapter = { for (var i = 0; i < changedTouches.length; i++) { let touch = changedTouches[i]; let touchPoint = new TouchPoint(touch, timeStamp, this._dpi); - this._touchPoints[touch.identifier || this.HOVER_ID] = touchPoint; + let identifier = (touch.identifier == undefined) ? + this.HOVER_ID : touch.identifier; + this._touchPoints[identifier] = touchPoint; this._lastExploreTime = timeStamp + this.SWIPE_MAX_DURATION; } this._dwellTimeout = this.chromeWin.setTimeout( @@ -140,7 +136,9 @@ this.TouchAdapter = { case 'touchmove': for (var i = 0; i < changedTouches.length; i++) { let touch = changedTouches[i]; - let touchPoint = this._touchPoints[touch.identifier || this.HOVER_ID]; + let identifier = (touch.identifier == undefined) ? + this.HOVER_ID : touch.identifier; + let touchPoint = this._touchPoints[identifier]; if (touchPoint) touchPoint.update(touch, timeStamp); } @@ -153,7 +151,9 @@ this.TouchAdapter = { case 'touchend': for (var i = 0; i < changedTouches.length; i++) { let touch = changedTouches[i]; - let touchPoint = this._touchPoints[touch.identifier || this.HOVER_ID]; + let identifier = (touch.identifier == undefined) ? + this.HOVER_ID : touch.identifier; + let touchPoint = this._touchPoints[identifier]; if (touchPoint) { touchPoint.update(touch, timeStamp); touchPoint.finish(); From 42f2ca6fe60bb159bdbe2825531cb8f801f13e9d Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Thu, 20 Dec 2012 14:47:07 -0500 Subject: [PATCH 016/217] Bug 807678: Don't proliferate NSPR DNS types beyond the host resolver itself. Make it possible to easily hook up other host resolvers. r=sworkman --- netwerk/base/public/nsINetAddr.idl | 8 +- netwerk/base/public/nsISocketTransport.idl | 11 +- netwerk/base/src/ProxyAutoConfig.cpp | 17 +- netwerk/base/src/ProxyAutoConfig.h | 5 +- netwerk/base/src/nsNetAddr.cpp | 59 ++--- netwerk/base/src/nsNetAddr.h | 6 +- netwerk/base/src/nsServerSocket.cpp | 8 +- netwerk/base/src/nsSocketTransport2.cpp | 48 ++-- netwerk/base/src/nsSocketTransport2.h | 5 +- netwerk/dns/DNS.cpp | 205 ++++++++++++++++++ netwerk/dns/DNS.h | 141 ++++++++++++ netwerk/dns/Makefile.in | 9 +- netwerk/dns/nsDNSService2.cpp | 121 +++++------ netwerk/dns/nsHostResolver.cpp | 139 ++++++------ netwerk/dns/nsHostResolver.h | 11 +- netwerk/dns/nsIDNSRecord.idl | 13 +- netwerk/ipc/NeckoMessageUtils.h | 43 ++-- .../protocol/ftp/nsFtpConnectionThread.cpp | 25 ++- netwerk/protocol/ftp/nsFtpConnectionThread.h | 3 +- netwerk/protocol/http/HttpBaseChannel.cpp | 16 +- netwerk/protocol/http/HttpBaseChannel.h | 9 +- netwerk/protocol/http/HttpChannelChild.cpp | 17 +- netwerk/protocol/http/HttpChannelChild.h | 9 +- netwerk/protocol/http/PHttpChannel.ipdl | 7 +- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 7 +- netwerk/socket/nsISOCKSSocketInfo.idl | 13 +- netwerk/socket/nsSOCKSIOLayer.cpp | 184 +++++++++------- 27 files changed, 777 insertions(+), 362 deletions(-) create mode 100644 netwerk/dns/DNS.cpp create mode 100644 netwerk/dns/DNS.h diff --git a/netwerk/base/public/nsINetAddr.idl b/netwerk/base/public/nsINetAddr.idl index c7e128452b62..bb2847a78f15 100644 --- a/netwerk/base/public/nsINetAddr.idl +++ b/netwerk/base/public/nsINetAddr.idl @@ -6,12 +6,10 @@ #include "nsISupports.idl" -native PRNetAddr(union PRNetAddr); - /** * nsINetAddr * - * This interface represents a (native) PRNetAddr struct in a readonly + * This interface represents a native NetAddr struct in a readonly * interface. */ [scriptable, uuid(c407ab6c-c3ca-4cb2-a99b-a7dfbb88af33)] @@ -26,7 +24,7 @@ interface nsINetAddr : nsISupports /** * @return Either the IP address (FAMILY_INET, FAMILY_INET6) or the path * (FAMILY_LOCAL) in string form. IP addresses are in the format produced by - * PR_NetAddrToString. + * mozilla::net::NetAddrToString. * * Note: Paths for FAMILY_LOCAL may have length limitations which are * implementation dependent and not documented as part of this interface. @@ -61,7 +59,7 @@ interface nsINetAddr : nsISupports /** * Network address families. These correspond to all the network address - * families supported by the PRNetAddr struct. + * families supported by the NetAddr struct. */ const unsigned long FAMILY_INET = 1; const unsigned long FAMILY_INET6 = 2; diff --git a/netwerk/base/public/nsISocketTransport.idl b/netwerk/base/public/nsISocketTransport.idl index 2a848a352d97..cf43807f5b84 100644 --- a/netwerk/base/public/nsISocketTransport.idl +++ b/netwerk/base/public/nsISocketTransport.idl @@ -8,7 +8,10 @@ interface nsIInterfaceRequestor; -native PRNetAddr(union PRNetAddr); +%{ C++ +#include "mozilla/net/DNS.h" +%} +native NetAddr(mozilla::net::NetAddr); /** * nsISocketTransport @@ -20,7 +23,7 @@ native PRNetAddr(union PRNetAddr); * NOTE: This is a free-threaded interface, meaning that the methods on * this interface may be called from any thread. */ -[scriptable, uuid(91ca3523-fc52-4dea-b167-ef8f56473dde)] +[scriptable, uuid(A8318027-0DDC-4E60-A89B-D81AFE3B5020)] interface nsISocketTransport : nsITransport { /** @@ -37,13 +40,13 @@ interface nsISocketTransport : nsITransport * Returns the IP address of the socket connection peer. This * attribute is defined only once a connection has been established. */ - [noscript] PRNetAddr getPeerAddr(); + [noscript] NetAddr getPeerAddr(); /** * Returns the IP address of the initiating end. This attribute * is defined only once a connection has been established. */ - [noscript] PRNetAddr getSelfAddr(); + [noscript] NetAddr getSelfAddr(); /** * Returns a scriptable version of getPeerAddr. This attribute is defined diff --git a/netwerk/base/src/ProxyAutoConfig.cpp b/netwerk/base/src/ProxyAutoConfig.cpp index 871bbf004fa3..5fbb4e15a4c9 100644 --- a/netwerk/base/src/ProxyAutoConfig.cpp +++ b/netwerk/base/src/ProxyAutoConfig.cpp @@ -15,6 +15,7 @@ #include "nsJSUtils.h" #include "prnetdb.h" #include "nsITimer.h" +#include "mozilla/net/DNS.h" namespace mozilla { namespace net { @@ -313,7 +314,7 @@ PACErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) // timeout of 0 means the normal necko timeout strategy, otherwise the dns request // will be canceled after aTimeout milliseconds static -JSBool PACResolve(const nsCString &aHostName, PRNetAddr *aNetAddr, +JSBool PACResolve(const nsCString &aHostName, NetAddr *aNetAddr, unsigned int aTimeout) { if (!sRunning) { @@ -326,7 +327,7 @@ JSBool PACResolve(const nsCString &aHostName, PRNetAddr *aNetAddr, bool ProxyAutoConfig::ResolveAddress(const nsCString &aHostName, - PRNetAddr *aNetAddr, + NetAddr *aNetAddr, unsigned int aTimeout) { nsCOMPtr dns = do_GetService(NS_DNSSERVICE_CONTRACTID); @@ -366,12 +367,12 @@ bool PACResolveToString(const nsCString &aHostName, nsCString &aDottedDecimal, unsigned int aTimeout) { - PRNetAddr netAddr; + NetAddr netAddr; if (!PACResolve(aHostName, &netAddr, aTimeout)) return false; char dottedDecimal[128]; - if (PR_NetAddrToString(&netAddr, dottedDecimal, sizeof(dottedDecimal)) != PR_SUCCESS) + if (!NetAddrToString(&netAddr, dottedDecimal, sizeof(dottedDecimal))) return false; aDottedDecimal.Assign(dottedDecimal); @@ -686,14 +687,16 @@ ProxyAutoConfig::Shutdown() } bool -ProxyAutoConfig::SrcAddress(const PRNetAddr *remoteAddress, nsCString &localAddress) +ProxyAutoConfig::SrcAddress(const NetAddr *remoteAddress, nsCString &localAddress) { PRFileDesc *fd; fd = PR_OpenUDPSocket(remoteAddress->raw.family); if (!fd) return false; - if (PR_Connect(fd, remoteAddress, 0) != PR_SUCCESS) { + PRNetAddr prRemoteAddress; + NetAddrToPRNetAddr(remoteAddress, &prRemoteAddress); + if (PR_Connect(fd, &prRemoteAddress, 0) != PR_SUCCESS) { PR_Close(fd); return false; } @@ -724,7 +727,7 @@ ProxyAutoConfig::MyIPAddressTryHost(const nsCString &hostName, unsigned int timeout, jsval *vp) { - PRNetAddr remoteAddress; + NetAddr remoteAddress; nsAutoCString localDottedDecimal; JSContext *cx = mJSRuntime->Context(); diff --git a/netwerk/base/src/ProxyAutoConfig.h b/netwerk/base/src/ProxyAutoConfig.h index 8f335ca82f14..9108d5d0fc99 100644 --- a/netwerk/base/src/ProxyAutoConfig.h +++ b/netwerk/base/src/ProxyAutoConfig.h @@ -12,6 +12,7 @@ #include "prio.h" #include "nsITimer.h" #include "nsAutoPtr.h" +#include "mozilla/net/DNS.h" namespace mozilla { namespace net { @@ -38,7 +39,7 @@ public: void GC(); bool MyIPAddress(jsval *vp); bool ResolveAddress(const nsCString &aHostName, - PRNetAddr *aNetAddr, unsigned int aTimeout); + NetAddr *aNetAddr, unsigned int aTimeout); /** * Get the proxy string for the specified URI. The proxy string is @@ -84,7 +85,7 @@ private: // used to compile the PAC file and setup the execution context nsresult SetupJS(); - bool SrcAddress(const PRNetAddr *remoteAddress, nsCString &localAddress); + bool SrcAddress(const NetAddr *remoteAddress, nsCString &localAddress); bool MyIPAddressTryHost(const nsCString &hostName, unsigned int timeout, jsval *vp); diff --git a/netwerk/base/src/nsNetAddr.cpp b/netwerk/base/src/nsNetAddr.cpp index 71fb01087c3f..49d6c89b1066 100644 --- a/netwerk/base/src/nsNetAddr.cpp +++ b/netwerk/base/src/nsNetAddr.cpp @@ -6,13 +6,14 @@ #include "nsNetAddr.h" #include "nsString.h" - #include "prnetdb.h" +using namespace mozilla::net; + NS_IMPL_ISUPPORTS1(nsNetAddr, nsINetAddr) /* Makes a copy of |addr| */ -nsNetAddr::nsNetAddr(PRNetAddr* addr) +nsNetAddr::nsNetAddr(NetAddr* addr) { NS_ASSERTION(addr, "null addr"); mAddr = *addr; @@ -22,15 +23,17 @@ nsNetAddr::nsNetAddr(PRNetAddr* addr) NS_IMETHODIMP nsNetAddr::GetFamily(uint16_t *aFamily) { switch(mAddr.raw.family) { - case PR_AF_INET: + case AF_INET: *aFamily = nsINetAddr::FAMILY_INET; break; - case PR_AF_INET6: + case AF_INET6: *aFamily = nsINetAddr::FAMILY_INET6; break; - case PR_AF_LOCAL: +#if defined(XP_UNIX) || defined(XP_OS2) + case AF_LOCAL: *aFamily = nsINetAddr::FAMILY_LOCAL; break; +#endif default: return NS_ERROR_UNEXPECTED; } @@ -43,18 +46,18 @@ NS_IMETHODIMP nsNetAddr::GetAddress(nsACString & aAddress) { switch(mAddr.raw.family) { /* PR_NetAddrToString can handle INET and INET6, but not LOCAL. */ - case PR_AF_INET: - aAddress.SetCapacity(16); - PR_NetAddrToString(&mAddr, aAddress.BeginWriting(), 16); + case AF_INET: + aAddress.SetCapacity(kIPv4CStrBufSize); + NetAddrToString(&mAddr, aAddress.BeginWriting(), kIPv4CStrBufSize); aAddress.SetLength(strlen(aAddress.BeginReading())); break; - case PR_AF_INET6: - aAddress.SetCapacity(46); - PR_NetAddrToString(&mAddr, aAddress.BeginWriting(), 46); + case AF_INET6: + aAddress.SetCapacity(kIPv6CStrBufSize); + NetAddrToString(&mAddr, aAddress.BeginWriting(), kIPv6CStrBufSize); aAddress.SetLength(strlen(aAddress.BeginReading())); break; #if defined(XP_UNIX) || defined(XP_OS2) - case PR_AF_LOCAL: + case AF_LOCAL: aAddress.Assign(mAddr.local.path); break; #endif @@ -70,15 +73,17 @@ NS_IMETHODIMP nsNetAddr::GetAddress(nsACString & aAddress) NS_IMETHODIMP nsNetAddr::GetPort(uint16_t *aPort) { switch(mAddr.raw.family) { - case PR_AF_INET: - *aPort = PR_ntohs(mAddr.inet.port); + case AF_INET: + *aPort = ntohs(mAddr.inet.port); break; - case PR_AF_INET6: - *aPort = PR_ntohs(mAddr.ipv6.port); + case AF_INET6: + *aPort = ntohs(mAddr.inet6.port); break; - case PR_AF_LOCAL: +#if defined(XP_UNIX) || defined(XP_OS2) + case AF_LOCAL: // There is no port number for local / connections. return NS_ERROR_NOT_AVAILABLE; +#endif default: return NS_ERROR_UNEXPECTED; } @@ -90,11 +95,13 @@ NS_IMETHODIMP nsNetAddr::GetPort(uint16_t *aPort) NS_IMETHODIMP nsNetAddr::GetFlow(uint32_t *aFlow) { switch(mAddr.raw.family) { - case PR_AF_INET6: - *aFlow = PR_ntohl(mAddr.ipv6.flowinfo); + case AF_INET6: + *aFlow = ntohl(mAddr.inet6.flowinfo); break; - case PR_AF_INET: - case PR_AF_LOCAL: + case AF_INET: +#if defined(XP_UNIX) || defined(XP_OS2) + case AF_LOCAL: +#endif // only for IPv6 return NS_ERROR_NOT_AVAILABLE; default: @@ -108,11 +115,13 @@ NS_IMETHODIMP nsNetAddr::GetFlow(uint32_t *aFlow) NS_IMETHODIMP nsNetAddr::GetScope(uint32_t *aScope) { switch(mAddr.raw.family) { - case PR_AF_INET6: - *aScope = PR_ntohl(mAddr.ipv6.scope_id); + case AF_INET6: + *aScope = ntohl(mAddr.inet6.scope_id); break; - case PR_AF_INET: - case PR_AF_LOCAL: + case AF_INET: +#if defined(XP_UNIX) || defined(XP_OS2) + case AF_LOCAL: +#endif // only for IPv6 return NS_ERROR_NOT_AVAILABLE; default: diff --git a/netwerk/base/src/nsNetAddr.h b/netwerk/base/src/nsNetAddr.h index 645cc8edb9c1..23db351dfe09 100644 --- a/netwerk/base/src/nsNetAddr.h +++ b/netwerk/base/src/nsNetAddr.h @@ -8,7 +8,7 @@ #define nsNetAddr_h__ #include "nsINetAddr.h" -#include "prio.h" +#include "mozilla/net/DNS.h" #include "mozilla/Attributes.h" class nsNetAddr MOZ_FINAL : public nsINetAddr @@ -17,10 +17,10 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSINETADDR - nsNetAddr(PRNetAddr* addr); + nsNetAddr(mozilla::net::NetAddr* addr); private: - PRNetAddr mAddr; + mozilla::net::NetAddr mAddr; protected: /* additional members */ diff --git a/netwerk/base/src/nsServerSocket.cpp b/netwerk/base/src/nsServerSocket.cpp index 4179299ac68b..822834107801 100644 --- a/netwerk/base/src/nsServerSocket.cpp +++ b/netwerk/base/src/nsServerSocket.cpp @@ -13,8 +13,10 @@ #include "prnetdb.h" #include "prio.h" #include "mozilla/Attributes.h" +#include "mozilla/net/DNS.h" using namespace mozilla; +using namespace mozilla::net; static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); @@ -167,9 +169,11 @@ nsServerSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags) } PRFileDesc *clientFD; - PRNetAddr clientAddr; + PRNetAddr prClientAddr; + NetAddr clientAddr; - clientFD = PR_Accept(mFD, &clientAddr, PR_INTERVAL_NO_WAIT); + clientFD = PR_Accept(mFD, &prClientAddr, PR_INTERVAL_NO_WAIT); + PRNetAddrToNetAddr(&prClientAddr, &clientAddr); if (!clientFD) { NS_WARNING("PR_Accept failed"); diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index dd7eafc1f41a..820ebbeaa56f 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -39,6 +39,7 @@ #endif using namespace mozilla; +using namespace mozilla::net; //----------------------------------------------------------------------------- @@ -806,22 +807,22 @@ nsSocketTransport::Init(const char **types, uint32_t typeCount, } nsresult -nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const PRNetAddr *addr) +nsSocketTransport::InitWithConnectedSocket(PRFileDesc *fd, const NetAddr *addr) { NS_ASSERTION(!mFD, "already initialized"); - char buf[64]; - PR_NetAddrToString(addr, buf, sizeof(buf)); + char buf[kIPv6CStrBufSize]; + NetAddrToString(addr, buf, sizeof(buf)); mHost.Assign(buf); uint16_t port; - if (addr->raw.family == PR_AF_INET) + if (addr->raw.family == AF_INET) port = addr->inet.port; else - port = addr->ipv6.port; - mPort = PR_ntohs(port); + port = addr->inet6.port; + mPort = ntohs(port); - memcpy(&mNetAddr, addr, sizeof(PRNetAddr)); + memcpy(&mNetAddr, addr, sizeof(NetAddr)); mPollFlags = (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT); mPollTimeout = mTimeouts[TIMEOUT_READ_WRITE]; @@ -907,7 +908,9 @@ nsSocketTransport::ResolveHost() // we send it when it's created, rather than the empty address // we send with the connect call. mState = STATE_RESOLVING; - PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, SocketPort(), &mNetAddr); + mNetAddr.raw.family = AF_INET; + mNetAddr.inet.port = htons(SocketPort()); + mNetAddr.inet.ip = htonl(INADDR_ANY); return PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nullptr); } } @@ -1052,7 +1055,7 @@ nsSocketTransport::InitiateSocket() nsresult rv; if (gIOService->IsOffline() && - !PR_IsNetAddrType(&mNetAddr, PR_IpAddrLoopback)) + !IsLoopBackAddress(&mNetAddr)) return NS_ERROR_OFFLINE; // @@ -1158,8 +1161,8 @@ nsSocketTransport::InitiateSocket() #if defined(PR_LOGGING) if (SOCKET_LOG_ENABLED()) { - char buf[64]; - PR_NetAddrToString(&mNetAddr, buf, sizeof(buf)); + char buf[kIPv6CStrBufSize]; + NetAddrToString(&mNetAddr, buf, sizeof(buf)); SOCKET_LOG((" trying address: %s\n", buf)); } #endif @@ -1167,7 +1170,9 @@ nsSocketTransport::InitiateSocket() // // Initiate the connect() to the host... // - status = PR_Connect(fd, &mNetAddr, NS_SOCKET_CONNECT_TIMEOUT); + PRNetAddr prAddr; + NetAddrToPRNetAddr(&mNetAddr, &prAddr); + status = PR_Connect(fd, &prAddr, NS_SOCKET_CONNECT_TIMEOUT); if (status == PR_SUCCESS) { // // we are connected! @@ -1679,7 +1684,7 @@ nsSocketTransport::IsLocal(bool *aIsLocal) { { MutexAutoLock lock(mLock); - *aIsLocal = PR_IsNetAddrType(&mNetAddr, PR_IpAddrLoopback); + *aIsLocal = IsLoopBackAddress(&mNetAddr); } } @@ -1900,7 +1905,7 @@ nsSocketTransport::GetPort(int32_t *port) } NS_IMETHODIMP -nsSocketTransport::GetPeerAddr(PRNetAddr *addr) +nsSocketTransport::GetPeerAddr(NetAddr *addr) { // once we are in the connected state, mNetAddr will not change. // so if we can verify that we are in the connected state, then @@ -1913,12 +1918,12 @@ nsSocketTransport::GetPeerAddr(PRNetAddr *addr) return NS_ERROR_NOT_AVAILABLE; } - memcpy(addr, &mNetAddr, sizeof(mNetAddr)); + memcpy(addr, &mNetAddr, sizeof(NetAddr)); return NS_OK; } NS_IMETHODIMP -nsSocketTransport::GetSelfAddr(PRNetAddr *addr) +nsSocketTransport::GetSelfAddr(NetAddr *addr) { // we must not call any PR methods on our file descriptor // while holding mLock since those methods might re-enter @@ -1930,11 +1935,14 @@ nsSocketTransport::GetSelfAddr(PRNetAddr *addr) fd = GetFD_Locked(); } - if (!fd) + if (!fd) { return NS_ERROR_NOT_CONNECTED; + } + PRNetAddr prAddr; nsresult rv = - (PR_GetSockName(fd, addr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; + (PR_GetSockName(fd, &prAddr) == PR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; + PRNetAddrToNetAddr(&prAddr, addr); { MutexAutoLock lock(mLock); @@ -1948,7 +1956,7 @@ nsSocketTransport::GetSelfAddr(PRNetAddr *addr) NS_IMETHODIMP nsSocketTransport::GetScriptablePeerAddr(nsINetAddr * *addr) { - PRNetAddr rawAddr; + NetAddr rawAddr; nsresult rv; rv = GetPeerAddr(&rawAddr); @@ -1964,7 +1972,7 @@ nsSocketTransport::GetScriptablePeerAddr(nsINetAddr * *addr) NS_IMETHODIMP nsSocketTransport::GetScriptableSelfAddr(nsINetAddr * *addr) { - PRNetAddr rawAddr; + NetAddr rawAddr; nsresult rv; rv = GetSelfAddr(&rawAddr); diff --git a/netwerk/base/src/nsSocketTransport2.h b/netwerk/base/src/nsSocketTransport2.h index 15126b9ea029..272244b33dfb 100644 --- a/netwerk/base/src/nsSocketTransport2.h +++ b/netwerk/base/src/nsSocketTransport2.h @@ -22,6 +22,7 @@ #include "nsIDNSRecord.h" #include "nsICancelable.h" #include "nsIClassInfo.h" +#include "mozilla/net/DNS.h" class nsSocketTransport; @@ -121,7 +122,7 @@ public: // this method instructs the socket transport to use an already connected // socket with the given address. nsresult InitWithConnectedSocket(PRFileDesc *socketFD, - const PRNetAddr *addr); + const mozilla::net::NetAddr *addr); // nsASocketHandler methods: void OnSocketReady(PRFileDesc *, int16_t outFlags); @@ -199,7 +200,7 @@ private: // mNetAddr is valid from GetPeerAddr() once we have // reached STATE_TRANSFERRING. It must not change after that. - PRNetAddr mNetAddr; + mozilla::net::NetAddr mNetAddr; bool mNetAddrIsSet; // socket methods (these can only be called on the socket thread): diff --git a/netwerk/dns/DNS.cpp b/netwerk/dns/DNS.cpp new file mode 100644 index 000000000000..b06b7f19f093 --- /dev/null +++ b/netwerk/dns/DNS.cpp @@ -0,0 +1,205 @@ +/* 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/. */ + +#include "mozilla/net/DNS.h" + +#include "mozilla/Assertions.h" +#include "mozilla/mozalloc.h" +#include + +#ifdef XP_WIN +#include "Ws2tcpip.h" +#endif + +namespace mozilla { +namespace net { + +const char *inet_ntop_internal(int af, const void *src, char *dst, socklen_t size) +{ +#ifdef XP_WIN + if (af == AF_INET) { + struct sockaddr_in s; + memset(&s, 0, sizeof(s)); + s.sin_family = AF_INET; + memcpy(&s.sin_addr, src, sizeof(struct in_addr)); + int result = getnameinfo((struct sockaddr *)&s, sizeof(struct sockaddr_in), + dst, size, nullptr, 0, NI_NUMERICHOST); + if (result == 0) { + return dst; + } + } + else if (af == AF_INET6) { + struct sockaddr_in6 s; + memset(&s, 0, sizeof(s)); + s.sin6_family = AF_INET6; + memcpy(&s.sin6_addr, src, sizeof(struct in_addr6)); + int result = getnameinfo((struct sockaddr *)&s, sizeof(struct sockaddr_in6), + dst, size, nullptr, 0, NI_NUMERICHOST); + if (result == 0) { + return dst; + } + } + return nullptr; +#else + return inet_ntop(af, src, dst, size); +#endif +} + +// Copies the contents of a PRNetAddr to a NetAddr. +// Does not do a ptr safety check! +void PRNetAddrToNetAddr(const PRNetAddr *prAddr, NetAddr *addr) +{ + if (prAddr->raw.family == PR_AF_INET) { + addr->inet.family = AF_INET; + addr->inet.port = prAddr->inet.port; + addr->inet.ip = prAddr->inet.ip; + } + else if (prAddr->raw.family == PR_AF_INET6) { + addr->inet6.family = AF_INET6; + addr->inet6.port = prAddr->ipv6.port; + addr->inet6.flowinfo = prAddr->ipv6.flowinfo; + memcpy(&addr->inet6.ip, &prAddr->ipv6.ip, sizeof(addr->inet6.ip.u8)); + addr->inet6.scope_id = prAddr->ipv6.scope_id; + } +#if defined(XP_UNIX) || defined(XP_OS2) + else if (prAddr->raw.family == PR_AF_LOCAL) { + addr->local.family = AF_LOCAL; + memcpy(addr->local.path, prAddr->local.path, sizeof(addr->local.path)); + } +#endif +} + +// Copies the contents of a NetAddr to a PRNetAddr. +// Does not do a ptr safety check! +void NetAddrToPRNetAddr(const NetAddr *addr, PRNetAddr *prAddr) +{ + if (addr->raw.family == AF_INET) { + prAddr->inet.family = PR_AF_INET; + prAddr->inet.port = addr->inet.port; + prAddr->inet.ip = addr->inet.ip; + } + else if (addr->raw.family == AF_INET6) { + prAddr->ipv6.family = PR_AF_INET6; + prAddr->ipv6.port = addr->inet6.port; + prAddr->ipv6.flowinfo = addr->inet6.flowinfo; + memcpy(&prAddr->ipv6.ip, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); + prAddr->ipv6.scope_id = addr->inet6.scope_id; + } +#if defined(XP_UNIX) || defined(XP_OS2) + else if (addr->raw.family == AF_LOCAL) { + prAddr->local.family = PR_AF_LOCAL; + memcpy(prAddr->local.path, addr->local.path, sizeof(addr->local.path)); + } +#endif +} + +bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize) +{ + if (addr->raw.family == AF_INET) { + if (bufSize < INET_ADDRSTRLEN) { + return false; + } + struct in_addr nativeAddr = {}; + nativeAddr.s_addr = addr->inet.ip; + return !!inet_ntop_internal(AF_INET, &nativeAddr, buf, bufSize); + } + else if (addr->raw.family == AF_INET6) { + if (bufSize < INET6_ADDRSTRLEN) { + return false; + } + struct in6_addr nativeAddr = {}; + memcpy(&nativeAddr.s6_addr, &addr->inet6.ip, sizeof(addr->inet6.ip.u8)); + return !!inet_ntop_internal(AF_INET6, &nativeAddr, buf, bufSize); + } +#if defined(XP_UNIX) || defined(XP_OS2) + else if (addr->raw.family == AF_LOCAL) { + if (bufSize < sizeof(addr->local.path)) { + return false; + } + memcpy(buf, addr->local.path, bufSize); + return true; + } +#endif + return false; +} + +bool IsLoopBackAddress(const NetAddr *addr) +{ + if (addr->raw.family == AF_INET) { + return (addr->inet.ip == htonl(INADDR_LOOPBACK)); + } + else if (addr->raw.family == AF_INET6) { + if (IPv6ADDR_IS_LOOPBACK(&addr->inet6.ip)) { + return true; + } else if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) && + IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_LOOPBACK)) { + return true; + } + } + return false; +} + +bool IsIPAddrAny(const NetAddr *addr) +{ + if (addr->raw.family == AF_INET) { + if (addr->inet.ip == htonl(INADDR_ANY)) { + return true; + } + } + else if (addr->raw.family == AF_INET6) { + if (IPv6ADDR_IS_UNSPECIFIED(&addr->inet6.ip)) { + return true; + } else if (IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip) && + IPv6ADDR_V4MAPPED_TO_IPADDR(&addr->inet6.ip) == htonl(INADDR_ANY)) { + return true; + } + } + return false; +} + +bool IsIPAddrV4Mapped(const NetAddr *addr) +{ + if (addr->raw.family == AF_INET6) { + return IPv6ADDR_IS_V4MAPPED(&addr->inet6.ip); + } + return false; +} + +NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr) +{ + PRNetAddrToNetAddr(prNetAddr, &mAddress); +} + +NetAddrElement::~NetAddrElement() +{ +} + +AddrInfo::AddrInfo(const char *host, const PRAddrInfo *prAddrInfo) +{ + size_t hostlen = strlen(host); + mHostName = static_cast(moz_xmalloc(hostlen + 1)); + memcpy(mHostName, host, hostlen + 1); + + PRNetAddr tmpAddr; + void *iter = nullptr; + do { + iter = PR_EnumerateAddrInfo(iter, prAddrInfo, 0, &tmpAddr); + if (iter) { + NetAddrElement *addrElement = new NetAddrElement(&tmpAddr); + mAddresses.insertBack(addrElement); + } + } while (iter); +} + +AddrInfo::~AddrInfo() +{ + NetAddrElement *addrElement; + while ((addrElement = mAddresses.popLast())) { + delete addrElement; + } + free(mHostName); +} + +} // namespace dns +} // namespace mozilla diff --git a/netwerk/dns/DNS.h b/netwerk/dns/DNS.h new file mode 100644 index 000000000000..1b737bef29a8 --- /dev/null +++ b/netwerk/dns/DNS.h @@ -0,0 +1,141 @@ +/* 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/. */ + +#ifndef DNS_h_ +#define DNS_h_ + +#include "nscore.h" +#include "prio.h" +#include "prnetdb.h" +#include "mozilla/LinkedList.h" + +#if !defined(XP_WIN) && !defined(XP_OS2) +#include +#endif + +#ifdef XP_WIN +#include "Winsock2.h" +#endif + +#define IPv6ADDR_IS_LOOPBACK(a) \ + (((a)->u32[0] == 0) && \ + ((a)->u32[1] == 0) && \ + ((a)->u32[2] == 0) && \ + ((a)->u8[12] == 0) && \ + ((a)->u8[13] == 0) && \ + ((a)->u8[14] == 0) && \ + ((a)->u8[15] == 0x1U)) + +#define IPv6ADDR_IS_V4MAPPED(a) \ + (((a)->u32[0] == 0) && \ + ((a)->u32[1] == 0) && \ + ((a)->u8[8] == 0) && \ + ((a)->u8[9] == 0) && \ + ((a)->u8[10] == 0xff) && \ + ((a)->u8[11] == 0xff)) + +#define IPv6ADDR_V4MAPPED_TO_IPADDR(a) ((a)->u32[3]) + +#define IPv6ADDR_IS_UNSPECIFIED(a) \ + (((a)->u32[0] == 0) && \ + ((a)->u32[1] == 0) && \ + ((a)->u32[2] == 0) && \ + ((a)->u32[3] == 0)) + +namespace mozilla { +namespace net { + +// Required buffer size for text form of an IP address. +// Includes space for null termination. +const int kIPv4CStrBufSize = 16; +const int kIPv6CStrBufSize = 46; + +// This was all created at a time in which we were using NSPR for host +// resolution and we were propagating NSPR types like "PRAddrInfo" and +// "PRNetAddr" all over Gecko. This made it hard to use another host +// resolver -- we were locked into NSPR. The goal here is to get away +// from that. We'll translate what we get from NSPR or any other host +// resolution library into the types below and use them in Gecko. + +union IPv6Addr { + uint8_t u8[16]; + uint16_t u16[8]; + uint32_t u32[4]; + uint64_t u64[2]; +}; + +// This struct is similar to operating system structs like "sockaddr", used for +// things like "connect" and "getsockname". When tempted to cast or do dumb +// copies of this struct to another struct, bear compiler-computed padding +// in mind. The size of this struct, and the layout of the data in it, may +// not be what you expect. +union NetAddr { + struct { + uint16_t family; /* address family (0x00ff maskable) */ + char data[14]; /* raw address data */ + } raw; + struct { + uint16_t family; /* address family (AF_INET) */ + uint16_t port; /* port number */ + uint32_t ip; /* The actual 32 bits of address */ + } inet; + struct { + uint16_t family; /* address family (AF_INET6) */ + uint16_t port; /* port number */ + uint32_t flowinfo; /* routing information */ + IPv6Addr ip; /* the actual 128 bits of address */ + uint32_t scope_id; /* set of interfaces for a scope */ + } inet6; +#if defined(XP_UNIX) || defined(XP_OS2) + struct { /* Unix domain socket address */ + uint16_t family; /* address family (AF_UNIX) */ +#ifdef XP_OS2 + char path[108]; /* null-terminated pathname */ +#else + char path[104]; /* null-terminated pathname */ +#endif + } local; +#endif +}; + +// This class wraps a NetAddr union to provide C++ linked list +// capabilities and other methods. It is created from a PRNetAddr, +// which is converted to a mozilla::dns::NetAddr. +class NetAddrElement : public LinkedListElement { +public: + NetAddrElement(const PRNetAddr *prNetAddr); + ~NetAddrElement(); + + NetAddr mAddress; +}; + +class AddrInfo { +public: + AddrInfo(const char *host, const PRAddrInfo *prAddrInfo); + ~AddrInfo(); + + char *mHostName; + LinkedList mAddresses; +}; + +// Copies the contents of a PRNetAddr to a NetAddr. +// Does not do a ptr safety check! +void PRNetAddrToNetAddr(const PRNetAddr *prAddr, NetAddr *addr); + +// Copies the contents of a NetAddr to a PRNetAddr. +// Does not do a ptr safety check! +void NetAddrToPRNetAddr(const NetAddr *addr, PRNetAddr *prAddr); + +bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize); + +bool IsLoopBackAddress(const NetAddr *addr); + +bool IsIPAddrAny(const NetAddr *addr); + +bool IsIPAddrV4Mapped(const NetAddr *addr); + +} // namespace net +} // namespace mozilla + +#endif // DNS_h_ diff --git a/netwerk/dns/Makefile.in b/netwerk/dns/Makefile.in index 7e517c116473..743745fcba2d 100644 --- a/netwerk/dns/Makefile.in +++ b/netwerk/dns/Makefile.in @@ -27,9 +27,10 @@ XPIDLSRCS = \ $(NULL) CPPSRCS = \ + DNS.cpp \ nsIDNService.cpp \ - nsDNSService2.cpp \ nsHostResolver.cpp \ + nsDNSService2.cpp \ nsEffectiveTLDService.cpp \ $(NULL) @@ -39,6 +40,12 @@ CSRCS = \ punycode.c \ $(NULL) +EXPORTS_NAMESPACES = mozilla/net + +EXPORTS_mozilla/net = \ + DNS.h \ + $(null) + # we don't want the shared lib, but we want to force the creation of a # static lib. FORCE_STATIC_LIB = 1 diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 7af99e987b40..0412a9c1ec12 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -30,6 +30,7 @@ #include "mozilla/Attributes.h" using namespace mozilla; +using namespace mozilla::net; static const char kPrefDnsCacheEntries[] = "network.dnsCacheEntries"; static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; @@ -51,7 +52,6 @@ public: nsDNSRecord(nsHostRecord *hostRecord) : mHostRecord(hostRecord) , mIter(nullptr) - , mLastIter(nullptr) , mIterGenCnt(-1) , mDone(false) {} @@ -59,9 +59,7 @@ private: virtual ~nsDNSRecord() {} nsRefPtr mHostRecord; - void *mIter; // enum ptr for PR_EnumerateAddrInfo - void *mLastIter; // previous enum ptr, for use in - // getting addrinfo in ReportUnusable + NetAddrElement *mIter; int mIterGenCnt; // the generation count of // mHostRecord->addr_info when we // start iterating @@ -83,7 +81,7 @@ nsDNSRecord::GetCanonicalName(nsACString &result) { MutexAutoLock lock(mHostRecord->addr_info_lock); if (mHostRecord->addr_info) - cname = PR_GetCanonNameFromAddrInfo(mHostRecord->addr_info); + cname = mHostRecord->addr_info->mHostName; else cname = mHostRecord->host; result.Assign(cname); @@ -92,46 +90,45 @@ nsDNSRecord::GetCanonicalName(nsACString &result) } NS_IMETHODIMP -nsDNSRecord::GetNextAddr(uint16_t port, PRNetAddr *addr) +nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) { - // not a programming error to poke the DNS record when it has no more - // entries. just fail without any debug warnings. this enables consumers - // to enumerate the DNS record without calling HasMore. - if (mDone) + if (mDone) { return NS_ERROR_NOT_AVAILABLE; + } mHostRecord->addr_info_lock.Lock(); - bool startedFresh = !mIter; - if (mHostRecord->addr_info) { - if (!mIter) - mIterGenCnt = mHostRecord->addr_info_gencnt; - else if (mIterGenCnt != mHostRecord->addr_info_gencnt) { - // mHostRecord->addr_info has changed, so mIter is invalid. - // Restart the iteration. Alternatively, we could just fail. + if (mIterGenCnt != mHostRecord->addr_info_gencnt) { + // mHostRecord->addr_info has changed, restart the iteration. mIter = nullptr; mIterGenCnt = mHostRecord->addr_info_gencnt; - startedFresh = true; } + bool startedFresh = !mIter; + do { - mLastIter = mIter; - mIter = PR_EnumerateAddrInfo(mIter, mHostRecord->addr_info, - port, addr); + if (!mIter) { + mIter = mHostRecord->addr_info->mAddresses.getFirst(); + } else { + mIter = mIter->getNext(); + } } - while (mIter && mHostRecord->Blacklisted(addr)); + while (mIter && mHostRecord->Blacklisted(&mIter->mAddress)); - if (startedFresh && !mIter) { - // if everything was blacklisted we want to reset the blacklist (and + if (!mIter && startedFresh) { + // If everything was blacklisted we want to reset the blacklist (and // likely relearn it) and return the first address. That is better - // than nothing + // than nothing. mHostRecord->ResetBlacklist(); - mLastIter = nullptr; - mIter = PR_EnumerateAddrInfo(nullptr, mHostRecord->addr_info, - port, addr); + mIter = mHostRecord->addr_info->mAddresses.getFirst(); } - + + if (mIter) { + memcpy(addr, &mIter->mAddress, sizeof(NetAddr)); + } + mHostRecord->addr_info_lock.Unlock(); + if (!mIter) { mDone = true; return NS_ERROR_NOT_AVAILABLE; @@ -139,57 +136,61 @@ nsDNSRecord::GetNextAddr(uint16_t port, PRNetAddr *addr) } else { mHostRecord->addr_info_lock.Unlock(); + if (!mHostRecord->addr) { // Both mHostRecord->addr_info and mHostRecord->addr are null. // This can happen if mHostRecord->addr_info expired and the // attempt to reresolve it failed. return NS_ERROR_NOT_AVAILABLE; } - memcpy(addr, mHostRecord->addr, sizeof(PRNetAddr)); - // set given port - port = PR_htons(port); - if (addr->raw.family == PR_AF_INET) - addr->inet.port = port; - else - addr->ipv6.port = port; - mDone = true; // no iterations + memcpy(addr, mHostRecord->addr, sizeof(NetAddr)); + mDone = true; } - - return NS_OK; + + // set given port + port = htons(port); + if (addr->raw.family == AF_INET) { + addr->inet.port = port; + } + else if (addr->raw.family == AF_INET6) { + addr->inet6.port = port; + } + + return NS_OK; } NS_IMETHODIMP nsDNSRecord::GetNextAddrAsString(nsACString &result) { - PRNetAddr addr; + NetAddr addr; nsresult rv = GetNextAddr(0, &addr); if (NS_FAILED(rv)) return rv; - char buf[64]; - if (PR_NetAddrToString(&addr, buf, sizeof(buf)) == PR_SUCCESS) { + char buf[kIPv6CStrBufSize]; + if (NetAddrToString(&addr, buf, sizeof(buf))) { result.Assign(buf); return NS_OK; } - NS_ERROR("PR_NetAddrToString failed unexpectedly"); + NS_ERROR("NetAddrToString failed unexpectedly"); return NS_ERROR_FAILURE; // conversion failed for some reason } NS_IMETHODIMP nsDNSRecord::HasMore(bool *result) { - if (mDone) + if (mDone) { *result = false; - else { - // unfortunately, NSPR does not provide a way for us to determine if - // there is another address other than to simply get the next address. - void *iterCopy = mIter; - void *iterLastCopy = mLastIter; - PRNetAddr addr; - *result = NS_SUCCEEDED(GetNextAddr(0, &addr)); - mIter = iterCopy; // backup iterator - mLastIter = iterLastCopy; // backup iterator - mDone = false; + return NS_OK; } + + NetAddrElement *iterCopy = mIter; + + NetAddr addr; + *result = NS_SUCCEEDED(GetNextAddr(0, &addr)); + + mIter = iterCopy; + mDone = false; + return NS_OK; } @@ -197,7 +198,6 @@ NS_IMETHODIMP nsDNSRecord::Rewind() { mIter = nullptr; - mLastIter = nullptr; mIterGenCnt = -1; mDone = false; return NS_OK; @@ -208,7 +208,7 @@ nsDNSRecord::ReportUnusable(uint16_t aPort) { // right now we don't use the port in the blacklist - mHostRecord->addr_info_lock.Lock(); + MutexAutoLock lock(mHostRecord->addr_info_lock); // Check that we are using a real addr_info (as opposed to a single // constant address), and that the generation count is valid. Otherwise, @@ -216,14 +216,9 @@ nsDNSRecord::ReportUnusable(uint16_t aPort) if (mHostRecord->addr_info && mIterGenCnt == mHostRecord->addr_info_gencnt) { - PRNetAddr addr; - void *id = PR_EnumerateAddrInfo(mLastIter, mHostRecord->addr_info, - aPort, &addr); - if (id) - mHostRecord->ReportUnusable(&addr); + mHostRecord->ReportUnusable(&mIter->mAddress); } - - mHostRecord->addr_info_lock.Unlock(); + return NS_OK; } diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index c0f76536d467..2ab5875610ef 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -185,7 +185,7 @@ nsHostRecord::~nsHostRecord() } bool -nsHostRecord::Blacklisted(PRNetAddr *aQuery) +nsHostRecord::Blacklisted(NetAddr *aQuery) { // must call locked LOG(("Checking blacklist for host [%s], host record [%p].\n", host, this)); @@ -195,8 +195,8 @@ nsHostRecord::Blacklisted(PRNetAddr *aQuery) return false; } - char buf[64]; - if (PR_NetAddrToString(aQuery, buf, sizeof(buf)) != PR_SUCCESS) { + char buf[kIPv6CStrBufSize]; + if (!NetAddrToString(aQuery, buf, sizeof(buf))) { return false; } nsDependentCString strQuery(buf); @@ -212,13 +212,13 @@ nsHostRecord::Blacklisted(PRNetAddr *aQuery) } void -nsHostRecord::ReportUnusable(PRNetAddr *aAddress) +nsHostRecord::ReportUnusable(NetAddr *aAddress) { // must call locked LOG(("Adding address to blacklist for host [%s], host record [%p].\n", host, this)); - char buf[64]; - if (PR_NetAddrToString(aAddress, buf, sizeof(buf)) == PR_SUCCESS) { + char buf[kIPv6CStrBufSize]; + if (NetAddrToString(aAddress, buf, sizeof(buf))) { LOG(("Successfully adding address [%s] to blacklist for host [%s].\n", buf, host)); mBlacklistedItems.AppendElement(nsCString(buf)); } @@ -275,21 +275,20 @@ HostDB_ClearEntry(PLDHashTable *table, nsHostDBEnt *he = static_cast(entry); LOG(("Clearing cache db entry for host [%s].\n", he->rec->host)); #if defined(DEBUG) && defined(PR_LOGGING) - if (!he->rec->addr_info) { - LOG(("No address info for host [%s].\n", he->rec->host)); - } else { - int32_t now = (int32_t) NowInMinutes(); - int32_t diff = (int32_t) he->rec->expiration - now; - LOG(("Record for [%s] expires in %d minute(s).\n", he->rec->host, diff)); - void *iter = nullptr; - PRNetAddr addr; - char buf[64]; - for (;;) { - iter = PR_EnumerateAddrInfo(iter, he->rec->addr_info, 0, &addr); - if (!iter) - break; - PR_NetAddrToString(&addr, buf, sizeof(buf)); - LOG((" [%s]\n", buf)); + { + MutexAutoLock lock(he->rec->addr_info_lock); + if (!he->rec->addr_info) { + LOG(("No address info for host [%s].\n", he->rec->host)); + } else { + int32_t now = (int32_t) NowInMinutes(); + int32_t diff = (int32_t) he->rec->expiration - now; + LOG(("Record for [%s] expires in %d minute(s).\n", he->rec->host, diff)); + NetAddrElement *addrElement; + char buf[kIPv6CStrBufSize]; + while ((addrElement = he->rec->addr_info->mAddresses.popLast())) { + NetAddrToString(&addrElement->mAddress, buf, sizeof(buf)); + LOG((" [%s]\n", buf)); + } } } #endif @@ -506,10 +505,10 @@ nsHostResolver::ResolveHost(const char *host, if (mShutdown) rv = NS_ERROR_NOT_INITIALIZED; else { + // Used to try to parse to an IP address literal. PRNetAddr tempAddr; - - // unfortunately, PR_StringToNetAddr does not properly initialize - // the output buffer in the case of IPv6 input. see bug 223145. + // Unfortunately, PR_StringToNetAddr does not properly initialize + // the output buffer in the case of IPv6 input. See bug 223145. memset(&tempAddr, 0, sizeof(PRNetAddr)); // check to see if there is already an entry for this |host| @@ -572,11 +571,8 @@ nsHostResolver::ResolveHost(const char *host, else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) { // ok, just copy the result into the host record, and be done // with it! ;-) - he->rec->addr = (PRNetAddr *) malloc(sizeof(PRNetAddr)); - if (!he->rec->addr) - status = NS_ERROR_OUT_OF_MEMORY; - else - memcpy(he->rec->addr, &tempAddr, sizeof(PRNetAddr)); + he->rec->addr = new NetAddr(); + PRNetAddrToNetAddr(&tempAddr, he->rec->addr); // put reference to host record on stack... Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); @@ -820,7 +816,7 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result) } void -nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo *result) +nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *result) { // get the list of pending callbacks for this lookup, and notify // them that the lookup is complete. @@ -834,15 +830,15 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo // update record fields. We might have a rec->addr_info already if a // previous lookup result expired and we're reresolving it.. - PRAddrInfo *old_addr_info; + AddrInfo *old_addr_info; { MutexAutoLock lock(rec->addr_info_lock); old_addr_info = rec->addr_info; rec->addr_info = result; rec->addr_info_gencnt++; } - if (old_addr_info) - PR_FreeAddrInfo(old_addr_info); + delete old_addr_info; + rec->expiration = NowInMinutes(); if (result) { rec->expiration += mMaxCacheLifetime; @@ -958,7 +954,7 @@ nsHostResolver::ThreadFunc(void *arg) #endif nsHostResolver *resolver = (nsHostResolver *)arg; nsHostRecord *rec; - PRAddrInfo *ai; + PRAddrInfo *prai = nullptr; while (resolver->GetHostToLookup(&rec)) { LOG(("Calling getaddrinfo for host [%s].\n", rec->host)); @@ -968,18 +964,21 @@ nsHostResolver::ThreadFunc(void *arg) TimeStamp startTime = TimeStamp::Now(); - ai = PR_GetAddrInfoByName(rec->host, rec->af, flags); + prai = PR_GetAddrInfoByName(rec->host, rec->af, flags); #if defined(RES_RETRY_ON_FAILURE) - if (!ai && rs.Reset()) - ai = PR_GetAddrInfoByName(rec->host, rec->af, flags); + if (!prai && rs.Reset()) + prai = PR_GetAddrInfoByName(rec->host, rec->af, flags); #endif TimeDuration elapsed = TimeStamp::Now() - startTime; uint32_t millis = static_cast(elapsed.ToMilliseconds()); - // convert error code to nsresult. + // convert error code to nsresult nsresult status; - if (ai) { + AddrInfo *ai = nullptr; + if (prai) { + ai = new AddrInfo(rec->host, prai); + status = NS_OK; Telemetry::Accumulate(!rec->addr_info_gencnt ? @@ -1032,45 +1031,45 @@ PLDHashOperator CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry, uint32_t number, void *arg) { - nsHostDBEnt *ent = static_cast (entry); - nsTArray *args = - static_cast *> (arg); - nsHostRecord *rec = ent->rec; - // Without addr_info, there is no meaning of adding this entry - if (rec->addr_info) { - DNSCacheEntries info; - const char *hostname; - PRNetAddr addr; - - if (rec->host) - hostname = rec->host; - else // No need to add this entry if no host name is there - return PL_DHASH_NEXT; - - uint32_t now = NowInMinutes(); - info.expiration = ((int64_t) rec->expiration - now) * 60; + // We don't pay attention to address literals, only resolved domains. + // Also require a host. + nsHostRecord *rec = static_cast(entry)->rec; + if (!rec->addr_info || !rec->host) { + return PL_DHASH_NEXT; + } + DNSCacheEntries info; + info.hostname = rec->host; + info.family = rec->af; + info.expiration = ((int64_t)rec->expiration - NowInMinutes()) * 60; + if (info.expiration <= 0) { // We only need valid DNS cache entries - if (info.expiration <= 0) - return PL_DHASH_NEXT; + return PL_DHASH_NEXT; + } - info.family = rec->af; - info.hostname = hostname; + { + MutexAutoLock lock(rec->addr_info_lock); - { - MutexAutoLock lock(rec->addr_info_lock); - void *ptr = PR_EnumerateAddrInfo(nullptr, rec->addr_info, 0, &addr); - while (ptr) { - char buf[64]; - if (PR_NetAddrToString(&addr, buf, sizeof(buf)) == PR_SUCCESS) - info.hostaddr.AppendElement(buf); - ptr = PR_EnumerateAddrInfo(ptr, rec->addr_info, 0, &addr); + NetAddr *addr = nullptr; + NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst(); + if (addrElement) { + addr = &addrElement->mAddress; + } + while (addr) { + char buf[kIPv6CStrBufSize]; + if (NetAddrToString(addr, buf, sizeof(buf))) { + info.hostaddr.AppendElement(buf); + } + addrElement = addrElement->getNext(); + if (addrElement) { + addr = &addrElement->mAddress; } } - - args->AppendElement(info); } + nsTArray *args = static_cast *>(arg); + args->AppendElement(info); + return PL_DHASH_NEXT; } diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 6de07d6f755d..7499cf363476 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -17,6 +17,7 @@ #include "nsIDNSListener.h" #include "nsString.h" #include "nsTArray.h" +#include "mozilla/net/DNS.h" #include "mozilla/net/DashboardTypes.h" class nsHostResolver; @@ -69,8 +70,8 @@ public: */ Mutex addr_info_lock; int addr_info_gencnt; /* generation count of |addr_info| */ - PRAddrInfo *addr_info; - PRNetAddr *addr; + mozilla::net::AddrInfo *addr_info; + mozilla::net::NetAddr *addr; bool negative; /* True if this record is a cache of a failed lookup. Negative cache entries are valid just like any other (though never for more than 60 seconds), but a use @@ -81,9 +82,9 @@ public: bool HasResult() const { return addr_info || addr || negative; } // hold addr_info_lock when calling the blacklist functions - bool Blacklisted(PRNetAddr *query); + bool Blacklisted(mozilla::net::NetAddr *query); void ResetBlacklist(); - void ReportUnusable(PRNetAddr *addr); + void ReportUnusable(mozilla::net::NetAddr *addr); private: friend class nsHostResolver; @@ -238,7 +239,7 @@ private: nsresult Init(); nsresult IssueLookup(nsHostRecord *); bool GetHostToLookup(nsHostRecord **m); - void OnLookupComplete(nsHostRecord *, nsresult, PRAddrInfo *); + void OnLookupComplete(nsHostRecord *, nsresult, mozilla::net::AddrInfo *); void DeQueue(PRCList &aQ, nsHostRecord **aResult); void ClearPendingQueue(PRCList *aPendingQueue); nsresult ConditionallyCreateThread(nsHostRecord *rec); diff --git a/netwerk/dns/nsIDNSRecord.idl b/netwerk/dns/nsIDNSRecord.idl index 263384ceafac..68b89d8a73a0 100644 --- a/netwerk/dns/nsIDNSRecord.idl +++ b/netwerk/dns/nsIDNSRecord.idl @@ -4,7 +4,10 @@ #include "nsISupports.idl" -native PRNetAddr(union PRNetAddr); +%{ C++ +#include "mozilla/net/DNS.h" +%} +native NetAddr(mozilla::net::NetAddr); /** * nsIDNSRecord @@ -14,7 +17,7 @@ native PRNetAddr(union PRNetAddr); * like an enumerator, allowing the caller to easily step through the * list of IP addresses. */ -[scriptable, uuid(ead9e9d8-7eef-4dae-a7f0-a1edcfb20478)] +[scriptable, uuid(67E6CF03-12C7-4AF4-AE5F-AE362C7AF8FF)] interface nsIDNSRecord : nsISupports { /** @@ -27,15 +30,15 @@ interface nsIDNSRecord : nsISupports /** * this function copies the value of the next IP address into the - * given PRNetAddr struct and increments the internal address iterator. + * given NetAddr struct and increments the internal address iterator. * * @param aPort - * A port number to initialize the PRNetAddr with. + * A port number to initialize the NetAddr with. * * @throws NS_ERROR_NOT_AVAILABLE if there is not another IP address in * the record. */ - [noscript] PRNetAddr getNextAddr(in uint16_t aPort); + [noscript] NetAddr getNextAddr(in uint16_t aPort); /** * this function returns the value of the next IP address as a diff --git a/netwerk/ipc/NeckoMessageUtils.h b/netwerk/ipc/NeckoMessageUtils.h index f7b20fe5ccb2..93528608bd2e 100644 --- a/netwerk/ipc/NeckoMessageUtils.h +++ b/netwerk/ipc/NeckoMessageUtils.h @@ -11,6 +11,7 @@ #include "ipc/IPCMessageUtils.h" #include "nsStringGlue.h" #include "prio.h" +#include "mozilla/net/DNS.h" namespace IPC { @@ -85,24 +86,24 @@ struct ParamTraits }; template<> -struct ParamTraits +struct ParamTraits { - static void Write(Message* aMsg, const PRNetAddr &aParam) + static void Write(Message* aMsg, const mozilla::net::NetAddr &aParam) { WriteParam(aMsg, aParam.raw.family); - if (aParam.raw.family == PR_AF_UNSPEC) { + if (aParam.raw.family == AF_UNSPEC) { aMsg->WriteBytes(aParam.raw.data, sizeof(aParam.raw.data)); - } else if (aParam.raw.family == PR_AF_INET) { + } else if (aParam.raw.family == AF_INET) { WriteParam(aMsg, aParam.inet.port); WriteParam(aMsg, aParam.inet.ip); - } else if (aParam.raw.family == PR_AF_INET6) { - WriteParam(aMsg, aParam.ipv6.port); - WriteParam(aMsg, aParam.ipv6.flowinfo); - WriteParam(aMsg, aParam.ipv6.ip.pr_s6_addr64[0]); - WriteParam(aMsg, aParam.ipv6.ip.pr_s6_addr64[1]); - WriteParam(aMsg, aParam.ipv6.scope_id); + } else if (aParam.raw.family == AF_INET6) { + WriteParam(aMsg, aParam.inet6.port); + WriteParam(aMsg, aParam.inet6.flowinfo); + WriteParam(aMsg, aParam.inet6.ip.u64[0]); + WriteParam(aMsg, aParam.inet6.ip.u64[1]); + WriteParam(aMsg, aParam.inet6.scope_id); #if defined(XP_UNIX) || defined(XP_OS2) - } else if (aParam.raw.family == PR_AF_LOCAL) { + } else if (aParam.raw.family == AF_LOCAL) { // Train's already off the rails: let's get a stack trace at least... NS_RUNTIMEABORT("Error: please post stack trace to " "https://bugzilla.mozilla.org/show_bug.cgi?id=661158"); @@ -114,26 +115,26 @@ struct ParamTraits * we can do but let the deserializer fail when it gets this message */ } - static bool Read(const Message* aMsg, void** aIter, PRNetAddr* aResult) + static bool Read(const Message* aMsg, void** aIter, mozilla::net::NetAddr* aResult) { if (!ReadParam(aMsg, aIter, &aResult->raw.family)) return false; - if (aResult->raw.family == PR_AF_UNSPEC) { + if (aResult->raw.family == AF_UNSPEC) { return aMsg->ReadBytes(aIter, reinterpret_cast(&aResult->raw.data), sizeof(aResult->raw.data)); - } else if (aResult->raw.family == PR_AF_INET) { + } else if (aResult->raw.family == AF_INET) { return ReadParam(aMsg, aIter, &aResult->inet.port) && ReadParam(aMsg, aIter, &aResult->inet.ip); - } else if (aResult->raw.family == PR_AF_INET6) { - return ReadParam(aMsg, aIter, &aResult->ipv6.port) && - ReadParam(aMsg, aIter, &aResult->ipv6.flowinfo) && - ReadParam(aMsg, aIter, &aResult->ipv6.ip.pr_s6_addr64[0]) && - ReadParam(aMsg, aIter, &aResult->ipv6.ip.pr_s6_addr64[1]) && - ReadParam(aMsg, aIter, &aResult->ipv6.scope_id); + } else if (aResult->raw.family == AF_INET6) { + return ReadParam(aMsg, aIter, &aResult->inet6.port) && + ReadParam(aMsg, aIter, &aResult->inet6.flowinfo) && + ReadParam(aMsg, aIter, &aResult->inet6.ip.u64[0]) && + ReadParam(aMsg, aIter, &aResult->inet6.ip.u64[1]) && + ReadParam(aMsg, aIter, &aResult->inet6.scope_id); #if defined(XP_UNIX) || defined(XP_OS2) - } else if (aResult->raw.family == PR_AF_LOCAL) { + } else if (aResult->raw.family == AF_LOCAL) { return aMsg->ReadBytes(aIter, reinterpret_cast(&aResult->local.path), sizeof(aResult->local.path)); diff --git a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp index f26b03a9e4be..5340ec6cdc0a 100644 --- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp +++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp @@ -42,6 +42,8 @@ extern PRLogModuleInfo* gFTPLog; #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) #define LOG_ALWAYS(args) PR_LOG(gFTPLog, PR_LOG_ALWAYS, args) +using namespace mozilla::net; + // remove FTP parameters (starting with ";") from the path static void removeParamsFromPath(nsCString& path) @@ -1292,7 +1294,9 @@ nsFtpState::S_pasv() { if (!mAddressChecked) { // Find socket address mAddressChecked = true; - PR_InitializeNetAddr(PR_IpAddrAny, 0, &mServerAddress); + mServerAddress.raw.family = AF_INET; + mServerAddress.inet.ip = htonl(INADDR_ANY); + mServerAddress.inet.port = htons(0); nsITransport *controlSocket = mControlConnection->Transport(); if (!controlSocket) @@ -1304,9 +1308,9 @@ nsFtpState::S_pasv() { if (sTrans) { nsresult rv = sTrans->GetPeerAddr(&mServerAddress); if (NS_SUCCEEDED(rv)) { - if (!PR_IsNetAddrType(&mServerAddress, PR_IpAddrAny)) - mServerIsIPv6 = mServerAddress.raw.family == PR_AF_INET6 && - !PR_IsNetAddrType(&mServerAddress, PR_IpAddrV4Mapped); + if (!IsIPAddrAny(&mServerAddress)) + mServerIsIPv6 = (mServerAddress.raw.family == AF_INET6) && + !IsIPAddrV4Mapped(&mServerAddress); else { /* * In case of SOCKS5 remote DNS resolution, we do @@ -1315,12 +1319,11 @@ nsFtpState::S_pasv() { * socks server should also be IPv6, and this is the * self address of the transport. */ - PRNetAddr selfAddress; + NetAddr selfAddress; rv = sTrans->GetSelfAddr(&selfAddress); if (NS_SUCCEEDED(rv)) - mServerIsIPv6 = selfAddress.raw.family == PR_AF_INET6 - && !PR_IsNetAddrType(&selfAddress, - PR_IpAddrV4Mapped); + mServerIsIPv6 = (selfAddress.raw.family == AF_INET6) && + !IsIPAddrV4Mapped(&selfAddress); } } } @@ -1446,9 +1449,9 @@ nsFtpState::R_pasv() { nsCOMPtr strans; nsAutoCString host; - if (!PR_IsNetAddrType(&mServerAddress, PR_IpAddrAny)) { - char buf[64]; - PR_NetAddrToString(&mServerAddress, buf, sizeof(buf)); + if (!IsIPAddrAny(&mServerAddress)) { + char buf[kIPv6CStrBufSize]; + NetAddrToString(&mServerAddress, buf, sizeof(buf)); host.Assign(buf); } else { /* diff --git a/netwerk/protocol/ftp/nsFtpConnectionThread.h b/netwerk/protocol/ftp/nsFtpConnectionThread.h index 262c2ffb8172..8a8549c1c62c 100644 --- a/netwerk/protocol/ftp/nsFtpConnectionThread.h +++ b/netwerk/protocol/ftp/nsFtpConnectionThread.h @@ -30,6 +30,7 @@ #include "nsIPrompt.h" #include "nsITransport.h" #include "nsIProxyInfo.h" +#include "mozilla/net/DNS.h" #include "nsFtpControlConnection.h" @@ -258,7 +259,7 @@ private: static uint32_t mSessionStartTime; - PRNetAddr mServerAddress; + mozilla::net::NetAddr mServerAddress; // ***** control read gvars nsresult mControlStatus; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index ad5862dab0cf..efd586d3d706 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1274,8 +1274,8 @@ HttpBaseChannel::GetLocalAddress(nsACString& addr) if (mSelfAddr.raw.family == PR_AF_UNSPEC) return NS_ERROR_NOT_AVAILABLE; - addr.SetCapacity(64); - PR_NetAddrToString(&mSelfAddr, addr.BeginWriting(), 64); + addr.SetCapacity(kIPv6CStrBufSize); + NetAddrToString(&mSelfAddr, addr.BeginWriting(), kIPv6CStrBufSize); addr.SetLength(strlen(addr.BeginReading())); return NS_OK; @@ -1287,10 +1287,10 @@ HttpBaseChannel::GetLocalPort(int32_t* port) NS_ENSURE_ARG_POINTER(port); if (mSelfAddr.raw.family == PR_AF_INET) { - *port = (int32_t)PR_ntohs(mSelfAddr.inet.port); + *port = (int32_t)ntohs(mSelfAddr.inet.port); } else if (mSelfAddr.raw.family == PR_AF_INET6) { - *port = (int32_t)PR_ntohs(mSelfAddr.ipv6.port); + *port = (int32_t)ntohs(mSelfAddr.inet6.port); } else return NS_ERROR_NOT_AVAILABLE; @@ -1304,8 +1304,8 @@ HttpBaseChannel::GetRemoteAddress(nsACString& addr) if (mPeerAddr.raw.family == PR_AF_UNSPEC) return NS_ERROR_NOT_AVAILABLE; - addr.SetCapacity(64); - PR_NetAddrToString(&mPeerAddr, addr.BeginWriting(), 64); + addr.SetCapacity(kIPv6CStrBufSize); + NetAddrToString(&mPeerAddr, addr.BeginWriting(), kIPv6CStrBufSize); addr.SetLength(strlen(addr.BeginReading())); return NS_OK; @@ -1317,10 +1317,10 @@ HttpBaseChannel::GetRemotePort(int32_t* port) NS_ENSURE_ARG_POINTER(port); if (mPeerAddr.raw.family == PR_AF_INET) { - *port = (int32_t)PR_ntohs(mPeerAddr.inet.port); + *port = (int32_t)ntohs(mPeerAddr.inet.port); } else if (mPeerAddr.raw.family == PR_AF_INET6) { - *port = (int32_t)PR_ntohs(mPeerAddr.ipv6.port); + *port = (int32_t)ntohs(mPeerAddr.inet6.port); } else return NS_ERROR_NOT_AVAILABLE; diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index f1bb9f9ece4a..b91003e35faa 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -31,6 +31,7 @@ #include "mozilla/net/NeckoCommon.h" #include "nsThreadUtils.h" #include "PrivateBrowsingChannel.h" +#include "mozilla/net/DNS.h" namespace mozilla { namespace net { @@ -188,8 +189,8 @@ public: nsHttpResponseHead * GetResponseHead() const { return mResponseHead; } nsHttpRequestHead * GetRequestHead() { return &mRequestHead; } - const PRNetAddr& GetSelfAddr() { return mSelfAddr; } - const PRNetAddr& GetPeerAddr() { return mPeerAddr; } + const NetAddr& GetSelfAddr() { return mSelfAddr; } + const NetAddr& GetPeerAddr() { return mPeerAddr; } public: /* Necko internal use only... */ @@ -243,8 +244,8 @@ protected: nsCString mContentCharsetHint; nsCString mUserSetCookieHeader; - PRNetAddr mSelfAddr; - PRNetAddr mPeerAddr; + NetAddr mSelfAddr; + NetAddr mPeerAddr; // HTTP Upgrade Data nsCString mUpgradeProtocol; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 70442c232dda..89e19d14203f 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -18,6 +18,7 @@ #include "base/compiler_specific.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/URIUtils.h" +#include "mozilla/net/DNS.h" using namespace mozilla::dom; using namespace mozilla::ipc; @@ -173,8 +174,8 @@ class StartRequestEvent : public ChannelEvent const uint32_t& cacheExpirationTime, const nsCString& cachedCharset, const nsCString& securityInfoSerialization, - const PRNetAddr& selfAddr, - const PRNetAddr& peerAddr) + const NetAddr& selfAddr, + const NetAddr& peerAddr) : mChild(child) , mResponseHead(responseHead) , mRequestHeaders(requestHeaders) @@ -205,8 +206,8 @@ class StartRequestEvent : public ChannelEvent uint32_t mCacheExpirationTime; nsCString mCachedCharset; nsCString mSecurityInfoSerialization; - PRNetAddr mSelfAddr; - PRNetAddr mPeerAddr; + NetAddr mSelfAddr; + NetAddr mPeerAddr; }; bool @@ -218,8 +219,8 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, const uint32_t& cacheExpirationTime, const nsCString& cachedCharset, const nsCString& securityInfoSerialization, - const PRNetAddr& selfAddr, - const PRNetAddr& peerAddr) + const NetAddr& selfAddr, + const NetAddr& peerAddr) { if (mEventQ.ShouldEnqueue()) { mEventQ.Enqueue(new StartRequestEvent(this, responseHead, useResponseHead, @@ -245,8 +246,8 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, const uint32_t& cacheExpirationTime, const nsCString& cachedCharset, const nsCString& securityInfoSerialization, - const PRNetAddr& selfAddr, - const PRNetAddr& peerAddr) + const NetAddr& selfAddr, + const NetAddr& peerAddr) { LOG(("HttpChannelChild::RecvOnStartRequest [this=%x]\n", this)); diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 9a3561c9abeb..c6902911225e 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -27,6 +27,7 @@ #include "nsIAssociatedContentSecurity.h" #include "nsIChildChannel.h" #include "nsIHttpChannelChild.h" +#include "mozilla/net/DNS.h" namespace mozilla { namespace net { @@ -97,8 +98,8 @@ protected: const uint32_t& cacheExpirationTime, const nsCString& cachedCharset, const nsCString& securityInfoSerialization, - const PRNetAddr& selfAddr, - const PRNetAddr& peerAddr); + const mozilla::net::NetAddr& selfAddr, + const mozilla::net::NetAddr& peerAddr); bool RecvOnTransportAndData(const nsresult& status, const uint64_t& progress, const uint64_t& progressMax, @@ -151,8 +152,8 @@ private: const uint32_t& cacheExpirationTime, const nsCString& cachedCharset, const nsCString& securityInfoSerialization, - const PRNetAddr& selfAddr, - const PRNetAddr& peerAddr); + const mozilla::net::NetAddr& selfAddr, + const mozilla::net::NetAddr& peerAddr); void OnTransportAndData(const nsresult& status, const uint64_t progress, const uint64_t& progressMax, diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index 5c84e5dcdb43..6df6d55b2b07 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -13,13 +13,14 @@ include protocol PBlob; //FIXME: bug #792908 include "mozilla/net/PHttpChannelParams.h"; include "mozilla/net/NeckoMessageUtils.h"; +include "mozilla/net/DNS.h"; include "prio.h"; using RequestHeaderTuples; using nsHttpHeaderArray; using nsHttpResponseHead; using nsHttpAtom; -using PRNetAddr; +using mozilla::net::NetAddr; namespace mozilla { namespace net { @@ -102,8 +103,8 @@ child: uint32_t cacheExpirationTime, nsCString cachedCharset, nsCString securityInfoSerialization, - PRNetAddr selfAddr, - PRNetAddr peerAddr); + NetAddr selfAddr, + NetAddr peerAddr); // Combines a single OnDataAvailable and its associated OnProgress & // OnStatus calls into one IPDL message diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index db0d609ddeb6..4aa819a3f3f4 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -11,6 +11,7 @@ #include "nsNetCID.h" #include "nsCOMPtr.h" #include "nsNetUtil.h" +#include "mozilla/net/DNS.h" #include "nsIServiceManager.h" @@ -2838,11 +2839,11 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans, !mEnt->mConnInfo->UsingProxy() && mEnt->mCoalescingKey.IsEmpty()) { - PRNetAddr addr; + NetAddr addr; nsresult rv = mSocketTransport->GetPeerAddr(&addr); if (NS_SUCCEEDED(rv)) { - mEnt->mCoalescingKey.SetCapacity(72); - PR_NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), 64); + mEnt->mCoalescingKey.SetCapacity(kIPv6CStrBufSize + 26); + NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), kIPv6CStrBufSize); mEnt->mCoalescingKey.SetLength( strlen(mEnt->mCoalescingKey.BeginReading())); diff --git a/netwerk/socket/nsISOCKSSocketInfo.idl b/netwerk/socket/nsISOCKSSocketInfo.idl index e58fb5b18dc7..641c22e27945 100644 --- a/netwerk/socket/nsISOCKSSocketInfo.idl +++ b/netwerk/socket/nsISOCKSSocketInfo.idl @@ -6,12 +6,15 @@ #include "nsISupports.idl" -[ptr] native PRNetAddrPtr(union PRNetAddr); +%{ C++ +#include "mozilla/net/DNS.h" +%} +[ptr] native NetAddrPtr(mozilla::net::NetAddr); -[scriptable, uuid(8f755c44-1dd2-11b2-a613-91117453fa95)] +[scriptable, uuid(D5C0D1F9-22D7-47DC-BF91-D9AC6E1251A6)] interface nsISOCKSSocketInfo : nsISupports { - [noscript] attribute PRNetAddrPtr destinationAddr; - [noscript] attribute PRNetAddrPtr externalProxyAddr; - [noscript] attribute PRNetAddrPtr internalProxyAddr; + [noscript] attribute NetAddrPtr destinationAddr; + [noscript] attribute NetAddrPtr externalProxyAddr; + [noscript] attribute NetAddrPtr internalProxyAddr; }; diff --git a/netwerk/socket/nsSOCKSIOLayer.cpp b/netwerk/socket/nsSOCKSIOLayer.cpp index 485775a7f389..22c8b7f72627 100644 --- a/netwerk/socket/nsSOCKSIOLayer.cpp +++ b/netwerk/socket/nsSOCKSIOLayer.cpp @@ -19,9 +19,12 @@ #include "nsIDNSListener.h" #include "nsICancelable.h" #include "nsThreadUtils.h" +#include "mozilla/net/DNS.h" -static PRDescIdentity nsSOCKSIOLayerIdentity; -static PRIOMethods nsSOCKSIOLayerMethods; +using namespace mozilla::net; + +static PRDescIdentity nsSOCKSIOLayerIdentity; +static PRIOMethods nsSOCKSIOLayerMethods; static bool firstTime = true; static bool ipv6Supported = true; @@ -84,7 +87,7 @@ private: void HandshakeFinished(PRErrorCode err = 0); PRStatus StartDNS(PRFileDesc *fd); PRStatus ConnectToProxy(PRFileDesc *fd); - void FixupAddressFamily(PRFileDesc *fd, PRNetAddr *proxy); + void FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy); PRStatus ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags); PRStatus WriteV4ConnectRequest(); PRStatus ReadV4ConnectResponse(); @@ -98,15 +101,15 @@ private: void WriteUint8(uint8_t d); void WriteUint16(uint16_t d); void WriteUint32(uint32_t d); - void WriteNetAddr(const PRNetAddr *addr); - void WriteNetPort(const PRNetAddr *addr); + void WriteNetAddr(const NetAddr *addr); + void WriteNetPort(const NetAddr *addr); void WriteString(const nsACString &str); uint8_t ReadUint8(); uint16_t ReadUint16(); uint32_t ReadUint32(); - void ReadNetAddr(PRNetAddr *addr, uint16_t fam); - void ReadNetPort(PRNetAddr *addr); + void ReadNetAddr(NetAddr *addr, uint16_t fam); + void ReadNetPort(NetAddr *addr); void WantRead(uint32_t sz); PRStatus ReadFromSocket(PRFileDesc *fd); @@ -130,9 +133,9 @@ private: int32_t mVersion; // SOCKS version 4 or 5 int32_t mDestinationFamily; uint32_t mFlags; - PRNetAddr mInternalProxyAddr; - PRNetAddr mExternalProxyAddr; - PRNetAddr mDestinationAddr; + NetAddr mInternalProxyAddr; + NetAddr mExternalProxyAddr; + NetAddr mDestinationAddr; PRIntervalTime mTimeout; }; @@ -144,14 +147,23 @@ nsSOCKSSocketInfo::nsSOCKSSocketInfo() , mAmountToRead(0) , mProxyPort(-1) , mVersion(-1) - , mDestinationFamily(PR_AF_INET) + , mDestinationFamily(AF_INET) , mFlags(0) , mTimeout(PR_INTERVAL_NO_TIMEOUT) { mData = new uint8_t[BUFFER_SIZE]; - PR_InitializeNetAddr(PR_IpAddrAny, 0, &mInternalProxyAddr); - PR_InitializeNetAddr(PR_IpAddrAny, 0, &mExternalProxyAddr); - PR_InitializeNetAddr(PR_IpAddrAny, 0, &mDestinationAddr); + + mInternalProxyAddr.raw.family = AF_INET; + mInternalProxyAddr.inet.ip = htonl(INADDR_ANY); + mInternalProxyAddr.inet.port = htons(0); + + mExternalProxyAddr.raw.family = AF_INET; + mExternalProxyAddr.inet.ip = htonl(INADDR_ANY); + mExternalProxyAddr.inet.port = htons(0); + + mDestinationAddr.raw.family = AF_INET; + mDestinationAddr.inet.ip = htonl(INADDR_ANY); + mDestinationAddr.inet.port = htons(0); } void @@ -168,44 +180,44 @@ nsSOCKSSocketInfo::Init(int32_t version, int32_t family, const char *proxyHost, NS_IMPL_THREADSAFE_ISUPPORTS2(nsSOCKSSocketInfo, nsISOCKSSocketInfo, nsIDNSListener) NS_IMETHODIMP -nsSOCKSSocketInfo::GetExternalProxyAddr(PRNetAddr * *aExternalProxyAddr) +nsSOCKSSocketInfo::GetExternalProxyAddr(NetAddr * *aExternalProxyAddr) { - memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(PRNetAddr)); + memcpy(*aExternalProxyAddr, &mExternalProxyAddr, sizeof(NetAddr)); return NS_OK; } NS_IMETHODIMP -nsSOCKSSocketInfo::SetExternalProxyAddr(PRNetAddr *aExternalProxyAddr) +nsSOCKSSocketInfo::SetExternalProxyAddr(NetAddr *aExternalProxyAddr) { - memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(PRNetAddr)); + memcpy(&mExternalProxyAddr, aExternalProxyAddr, sizeof(NetAddr)); return NS_OK; } NS_IMETHODIMP -nsSOCKSSocketInfo::GetDestinationAddr(PRNetAddr * *aDestinationAddr) +nsSOCKSSocketInfo::GetDestinationAddr(NetAddr * *aDestinationAddr) { - memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(PRNetAddr)); + memcpy(*aDestinationAddr, &mDestinationAddr, sizeof(NetAddr)); return NS_OK; } NS_IMETHODIMP -nsSOCKSSocketInfo::SetDestinationAddr(PRNetAddr *aDestinationAddr) +nsSOCKSSocketInfo::SetDestinationAddr(NetAddr *aDestinationAddr) { - memcpy(&mDestinationAddr, aDestinationAddr, sizeof(PRNetAddr)); + memcpy(&mDestinationAddr, aDestinationAddr, sizeof(NetAddr)); return NS_OK; } NS_IMETHODIMP -nsSOCKSSocketInfo::GetInternalProxyAddr(PRNetAddr * *aInternalProxyAddr) +nsSOCKSSocketInfo::GetInternalProxyAddr(NetAddr * *aInternalProxyAddr) { - memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(PRNetAddr)); + memcpy(*aInternalProxyAddr, &mInternalProxyAddr, sizeof(NetAddr)); return NS_OK; } NS_IMETHODIMP -nsSOCKSSocketInfo::SetInternalProxyAddr(PRNetAddr *aInternalProxyAddr) +nsSOCKSSocketInfo::SetInternalProxyAddr(NetAddr *aInternalProxyAddr) { - memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(PRNetAddr)); + memcpy(&mInternalProxyAddr, aInternalProxyAddr, sizeof(NetAddr)); return NS_OK; } @@ -297,7 +309,7 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd) // Try socks5 if the destination addrress is IPv6 if (mVersion == 4 && - PR_NetAddrFamily(&mDestinationAddr) == PR_AF_INET6) { + mDestinationAddr.raw.family == AF_INET6) { mVersion = 5; } @@ -315,14 +327,16 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd) } #if defined(PR_LOGGING) - char buf[64]; - PR_NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf)); + char buf[kIPv6CStrBufSize]; + NetAddrToString(&mInternalProxyAddr, buf, sizeof(buf)); LOGDEBUG(("socks: trying proxy server, %s:%hu", - buf, PR_ntohs(PR_NetAddrInetPort(&mInternalProxyAddr)))); + buf, ntohs(mInternalProxyAddr.inet.port))); #endif - PRNetAddr proxy = mInternalProxyAddr; + NetAddr proxy = mInternalProxyAddr; FixupAddressFamily(fd, &proxy); - status = fd->lower->methods->connect(fd->lower, &proxy, mTimeout); + PRNetAddr prProxy; + NetAddrToPRNetAddr(&proxy, &prProxy); + status = fd->lower->methods->connect(fd->lower, &prProxy, mTimeout); if (status != PR_SUCCESS) { PRErrorCode c = PR_GetError(); // If EINPROGRESS, return now and check back later after polling @@ -340,25 +354,25 @@ nsSOCKSSocketInfo::ConnectToProxy(PRFileDesc *fd) } void -nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, PRNetAddr *proxy) +nsSOCKSSocketInfo::FixupAddressFamily(PRFileDesc *fd, NetAddr *proxy) { - int32_t proxyFamily = PR_NetAddrFamily(&mInternalProxyAddr); + int32_t proxyFamily = mInternalProxyAddr.raw.family; // Do nothing if the address family is already matched if (proxyFamily == mDestinationFamily) { return; } // If the system does not support IPv6 and the proxy address is IPv6, // We can do nothing here. - if (proxyFamily == PR_AF_INET6 && !ipv6Supported) { + if (proxyFamily == AF_INET6 && !ipv6Supported) { return; } // If the system does not support IPv6 and the destination address is // IPv6, convert IPv4 address to IPv4-mapped IPv6 address to satisfy // the emulation layer - if (mDestinationFamily == PR_AF_INET6 && !ipv6Supported) { - proxy->ipv6.family = PR_AF_INET6; - proxy->ipv6.port = mInternalProxyAddr.inet.port; - uint8_t *proxyp = proxy->ipv6.ip.pr_s6_addr; + if (mDestinationFamily == AF_INET6 && !ipv6Supported) { + proxy->inet6.family = AF_INET6; + proxy->inet6.port = mInternalProxyAddr.inet.port; + uint8_t *proxyp = proxy->inet6.ip.u8; memset(proxyp, 0, 10); memset(proxyp + 10, 0xff, 2); memcpy(proxyp + 12,(char *) &mInternalProxyAddr.inet.ip, 4); @@ -424,7 +438,7 @@ nsSOCKSSocketInfo::ContinueConnectingToProxy(PRFileDesc *fd, int16_t oflags) PRStatus nsSOCKSSocketInfo::WriteV4ConnectRequest() { - PRNetAddr *addr = &mDestinationAddr; + NetAddr *addr = &mDestinationAddr; int32_t proxy_resolve; NS_ABORT_IF_FALSE(mState == SOCKS_CONNECTING_TO_PROXY, @@ -448,7 +462,7 @@ nsSOCKSSocketInfo::WriteV4ConnectRequest() // four bytes set to 0 and the last byte set to something other // than 0, is used to notify the proxy that this is a SOCKS 4a // request. This request type works for Tor and perhaps others. - WriteUint32(PR_htonl(0x00000001)); // Fake IP + WriteUint32(htonl(0x00000001)); // Fake IP WriteUint8(0x00); // Send an emtpy username if (mDestinationHost.Length() > MAX_HOSTNAME_LEN) { LOGERROR(("socks4: destination host name is too long!")); @@ -457,10 +471,10 @@ nsSOCKSSocketInfo::WriteV4ConnectRequest() } WriteString(mDestinationHost); // Hostname WriteUint8(0x00); - } else if (PR_NetAddrFamily(addr) == PR_AF_INET) { + } else if (addr->raw.family == AF_INET) { WriteNetAddr(addr); // Add the IPv4 address WriteUint8(0x00); // Send an emtpy username - } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) { + } else if (addr->raw.family == AF_INET6) { LOGERROR(("socks: SOCKS 4 can't handle IPv6 addresses!")); HandshakeFinished(PR_BAD_ADDRESS_ERROR); return PR_FAILURE; @@ -544,7 +558,7 @@ PRStatus nsSOCKSSocketInfo::WriteV5ConnectRequest() { // Send SOCKS 5 connect request - PRNetAddr *addr = &mDestinationAddr; + NetAddr *addr = &mDestinationAddr; int32_t proxy_resolve; proxy_resolve = mFlags & nsISocketProvider::PROXY_RESOLVES_HOST; @@ -571,10 +585,10 @@ nsSOCKSSocketInfo::WriteV5ConnectRequest() WriteUint8(0x03); // addr type -- domainname WriteUint8(mDestinationHost.Length()); // name length WriteString(mDestinationHost); - } else if (PR_NetAddrFamily(addr) == PR_AF_INET) { + } else if (addr->raw.family == AF_INET) { WriteUint8(0x01); // addr type -- IPv4 WriteNetAddr(addr); - } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) { + } else if (addr->raw.family == AF_INET6) { WriteUint8(0x04); // addr type -- IPv6 WriteNetAddr(addr); } else { @@ -718,14 +732,14 @@ nsSOCKSSocketInfo::ReadV5ConnectResponseBottom() // Read what the proxy says is our source address switch (type) { case 0x01: // ipv4 - ReadNetAddr(&mExternalProxyAddr, PR_AF_INET); + ReadNetAddr(&mExternalProxyAddr, AF_INET); break; case 0x04: // ipv6 - ReadNetAddr(&mExternalProxyAddr, PR_AF_INET6); + ReadNetAddr(&mExternalProxyAddr, AF_INET6); break; case 0x03: // fqdn (skip) mReadOffset += len; - mExternalProxyAddr.raw.family = PR_AF_INET; + mExternalProxyAddr.raw.family = AF_INET; break; } @@ -865,17 +879,17 @@ nsSOCKSSocketInfo::WriteUint32(uint32_t v) } void -nsSOCKSSocketInfo::WriteNetAddr(const PRNetAddr *addr) +nsSOCKSSocketInfo::WriteNetAddr(const NetAddr *addr) { const char *ip = NULL; uint32_t len = 0; - if (PR_NetAddrFamily(addr) == PR_AF_INET) { + if (addr->raw.family == AF_INET) { ip = (const char*)&addr->inet.ip; len = sizeof(addr->inet.ip); - } else if (PR_NetAddrFamily(addr) == PR_AF_INET6) { - ip = (const char*)addr->ipv6.ip.pr_s6_addr; - len = sizeof(addr->ipv6.ip.pr_s6_addr); + } else if (addr->raw.family == AF_INET6) { + ip = (const char*)addr->inet6.ip.u8; + len = sizeof(addr->inet6.ip.u8); } NS_ABORT_IF_FALSE(ip != NULL, "Unknown address"); @@ -887,9 +901,9 @@ nsSOCKSSocketInfo::WriteNetAddr(const PRNetAddr *addr) } void -nsSOCKSSocketInfo::WriteNetPort(const PRNetAddr *addr) +nsSOCKSSocketInfo::WriteNetPort(const NetAddr *addr) { - WriteUint16(PR_NetAddrInetPort(addr)); + WriteUint16(addr->inet.port); } void @@ -935,29 +949,29 @@ nsSOCKSSocketInfo::ReadUint32() } void -nsSOCKSSocketInfo::ReadNetAddr(PRNetAddr *addr, uint16_t fam) +nsSOCKSSocketInfo::ReadNetAddr(NetAddr *addr, uint16_t fam) { uint32_t amt = 0; const uint8_t *ip = mData + mReadOffset; addr->raw.family = fam; - if (fam == PR_AF_INET) { + if (fam == AF_INET) { amt = sizeof(addr->inet.ip); NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength, "Not enough space to pop an ipv4 addr!"); memcpy(&addr->inet.ip, ip, amt); - } else if (fam == PR_AF_INET6) { - amt = sizeof(addr->ipv6.ip.pr_s6_addr); + } else if (fam == AF_INET6) { + amt = sizeof(addr->inet6.ip.u8); NS_ABORT_IF_FALSE(mReadOffset + amt <= mDataLength, "Not enough space to pop an ipv6 addr!"); - memcpy(addr->ipv6.ip.pr_s6_addr, ip, amt); + memcpy(addr->inet6.ip.u8, ip, amt); } mReadOffset += amt; } void -nsSOCKSSocketInfo::ReadNetPort(PRNetAddr *addr) +nsSOCKSSocketInfo::ReadNetPort(NetAddr *addr) { addr->inet.port = ReadUint16(); } @@ -1060,22 +1074,24 @@ static PRStatus nsSOCKSIOLayerConnect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime to) { PRStatus status; - PRNetAddr dst; + NetAddr dst; nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; if (info == NULL) return PR_FAILURE; - if (PR_NetAddrFamily(addr) == PR_AF_INET6 && + if (addr->raw.family == PR_AF_INET6 && PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) { const uint8_t *srcp; LOGDEBUG(("socks: converting ipv4-mapped ipv6 address to ipv4")); // copied from _PR_ConvertToIpv4NetAddr() - PR_InitializeNetAddr(PR_IpAddrAny, 0, &dst); + dst.raw.family = AF_INET; + dst.inet.ip = htonl(INADDR_ANY); + dst.inet.port = htons(0); srcp = addr->ipv6.ip.pr_s6_addr; memcpy(&dst.inet.ip, srcp + 12, 4); - dst.inet.family = PR_AF_INET; + dst.inet.family = AF_INET; dst.inet.port = addr->ipv6.port; } else { memcpy(&dst, addr, sizeof(dst)); @@ -1163,8 +1179,12 @@ nsSOCKSIOLayerGetName(PRFileDesc *fd, PRNetAddr *addr) nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; if (info != NULL && addr != NULL) { - if (info->GetExternalProxyAddr(&addr) == NS_OK) + NetAddr temp; + NetAddr *tempPtr = &temp; + if (info->GetExternalProxyAddr(&tempPtr) == NS_OK) { + NetAddrToPRNetAddr(tempPtr, addr); return PR_SUCCESS; + } } return PR_FAILURE; @@ -1176,8 +1196,12 @@ nsSOCKSIOLayerGetPeerName(PRFileDesc *fd, PRNetAddr *addr) nsSOCKSSocketInfo * info = (nsSOCKSSocketInfo*) fd->secret; if (info != NULL && addr != NULL) { - if (info->GetDestinationAddr(&addr) == NS_OK) + NetAddr temp; + NetAddr *tempPtr = &temp; + if (info->GetDestinationAddr(&tempPtr) == NS_OK) { + NetAddrToPRNetAddr(tempPtr, addr); return PR_SUCCESS; + } } return PR_FAILURE; @@ -1219,21 +1243,21 @@ nsSOCKSIOLayerAddToSocket(int32_t family, PR_Close(tmpfd); } - nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer"); - nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods(); + nsSOCKSIOLayerIdentity = PR_GetUniqueIdentity("SOCKS layer"); + nsSOCKSIOLayerMethods = *PR_GetDefaultIOMethods(); - nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect; - nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue; - nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll; - nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind; + nsSOCKSIOLayerMethods.connect = nsSOCKSIOLayerConnect; + nsSOCKSIOLayerMethods.connectcontinue = nsSOCKSIOLayerConnectContinue; + nsSOCKSIOLayerMethods.poll = nsSOCKSIOLayerPoll; + nsSOCKSIOLayerMethods.bind = nsSOCKSIOLayerBind; nsSOCKSIOLayerMethods.acceptread = nsSOCKSIOLayerAcceptRead; nsSOCKSIOLayerMethods.getsockname = nsSOCKSIOLayerGetName; nsSOCKSIOLayerMethods.getpeername = nsSOCKSIOLayerGetPeerName; - nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept; - nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen; - nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose; + nsSOCKSIOLayerMethods.accept = nsSOCKSIOLayerAccept; + nsSOCKSIOLayerMethods.listen = nsSOCKSIOLayerListen; + nsSOCKSIOLayerMethods.close = nsSOCKSIOLayerClose; - firstTime = false; + firstTime = false; #if defined(PR_LOGGING) gSOCKSLog = PR_NewLogModule("SOCKS"); @@ -1243,8 +1267,8 @@ nsSOCKSIOLayerAddToSocket(int32_t family, LOGDEBUG(("Entering nsSOCKSIOLayerAddToSocket().")); - PRFileDesc * layer; - PRStatus rv; + PRFileDesc *layer; + PRStatus rv; layer = PR_CreateIOLayerStub(nsSOCKSIOLayerIdentity, &nsSOCKSIOLayerMethods); if (! layer) From f2dfd3a5d76e764d414091d3247cd5b68c3f6591 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 20 Dec 2012 11:54:00 -0800 Subject: [PATCH 017/217] Bug 822558: Don't confuse subprocesses about ended touches. r=mwu a=blocking-basecamp --- dom/ipc/TabParent.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 492c14df6ee4..fa99bcd58905 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -431,14 +431,14 @@ bool TabParent::SendRealTouchEvent(nsTouchEvent& event) } nsTouchEvent e(event); - // PresShell::HandleEventInternal adds touches on touch end/cancel, - // when we're not capturing raw events from the widget backend. - // This hack filters those out. Bug 785554 - if (sEventCapturer != this && - (event.message == NS_TOUCH_END || event.message == NS_TOUCH_CANCEL)) { + // PresShell::HandleEventInternal adds touches on touch end/cancel. + // This confuses remote content into thinking that the added touches + // are part of the touchend/cancel, when actually they're not. + if (event.message == NS_TOUCH_END || event.message == NS_TOUCH_CANCEL) { for (int i = e.touches.Length() - 1; i >= 0; i--) { - if (!e.touches[i]->mChanged) + if (!e.touches[i]->mChanged) { e.touches.RemoveElementAt(i); + } } } From 5282f37ed8a3bc26e74a37b9893d88b3b465e554 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 20 Dec 2012 11:54:00 -0800 Subject: [PATCH 018/217] Bug 823422: Make ipdl C++ unit tests compile again. r=jlebar --- ipc/ipdl/test/cxx/TestDataStructures.cpp | 10 +++++----- ipc/ipdl/test/cxx/TestJSON.cpp | 15 ++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ipc/ipdl/test/cxx/TestDataStructures.cpp b/ipc/ipdl/test/cxx/TestDataStructures.cpp index 095bb7d09fd3..fe8757972320 100644 --- a/ipc/ipdl/test/cxx/TestDataStructures.cpp +++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp @@ -16,7 +16,7 @@ static const uint32_t nactors = 10; template static void -assert_arrays_equal(InfallibleTArray a, InfallibleTArray b) +assert_arrays_equal(const InfallibleTArray& a, const InfallibleTArray& b) { test_assert(a == b, "arrays equal"); } @@ -179,13 +179,13 @@ bool TestDataStructuresParent::RecvTest6( IntDoubleArrays id1(i1[0]); test_assert(42 == id1.get_int(), "wrong value"); - InfallibleTArray i2a = i1[1].get_ArrayOfint(); + InfallibleTArray i2a(i1[1].get_ArrayOfint()); test_assert(3 == i2a.Length(), "wrong length"); test_assert(1 == i2a[0], "wrong value"); test_assert(2 == i2a[1], "wrong value"); test_assert(3 == i2a[2], "wrong value"); - InfallibleTArray i3a = i1[2].get_ArrayOfdouble(); + InfallibleTArray i3a(i1[2].get_ArrayOfdouble()); test_assert(3 == i3a.Length(), "wrong length"); test_assert(1.0 == i3a[0], "wrong value"); test_assert(2.0 == i3a[1], "wrong value"); @@ -618,8 +618,8 @@ TestDataStructuresChild::Test6() test_assert(3 == o1.Length(), "wrong length"); IntDoubleArrays od1(o1[0]); - InfallibleTArray od2 = o1[1].get_ArrayOfint(); - InfallibleTArray od3 = o1[2].get_ArrayOfdouble(); + InfallibleTArray od2(o1[1].get_ArrayOfint()); + InfallibleTArray od3(o1[2].get_ArrayOfdouble()); test_assert(42 == od1.get_int(), "wrong value"); assert_arrays_equal(id2, od2); diff --git a/ipc/ipdl/test/cxx/TestJSON.cpp b/ipc/ipdl/test/cxx/TestJSON.cpp index 943e4cfc9bf2..ea742b705984 100644 --- a/ipc/ipdl/test/cxx/TestJSON.cpp +++ b/ipc/ipdl/test/cxx/TestJSON.cpp @@ -14,15 +14,12 @@ String(const char* const str) return NS_ConvertUTF8toUTF16(str); } -static InfallibleTArray -Array123() +static void +Array123(InfallibleTArray& a123) { - InfallibleTArray a123; a123.AppendElement(1); a123.AppendElement(2); a123.AppendElement(3); test_assert(a123 == a123, "operator== is broken"); - - return a123; } template @@ -55,7 +52,9 @@ MakeTestVariant(HandleT* handle) outer.AppendElement(handle); - outer.AppendElement(Array123()); + InfallibleTArray tmp; + Array123(tmp); + outer.AppendElement(tmp); InfallibleTArray obj; obj.AppendElement(KeyValue(String("undefined"), void_t())); @@ -64,7 +63,9 @@ MakeTestVariant(HandleT* handle) obj.AppendElement(KeyValue(String("1.25"), 1.25)); obj.AppendElement(KeyValue(String("string"), String("value"))); obj.AppendElement(KeyValue(String("handle"), handle)); - obj.AppendElement(KeyValue(String("array"), Array123())); + InfallibleTArray tmp2; + Array123(tmp2); + obj.AppendElement(KeyValue(String("array"), tmp2)); outer.AppendElement(obj); From b755435a535d418b625b228d84c6f0e1e2dfd2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Thu, 20 Dec 2012 12:08:58 -0800 Subject: [PATCH 019/217] Bug 823040 - we should update the app's manifest during update (for hosted apps with appcache) [r=ferjm] --- dom/apps/src/Webapps.jsm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 14b60db4c141..3d929f98ffbc 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -1122,10 +1122,12 @@ this.DOMApplicationRegistry = { aData.event = "downloadavailable"; aData.app = { downloadAvailable: true, - downloadSize: manifest.size + downloadSize: manifest.size, + updateManifest: aManifest } DOMApplicationRegistry._saveApps(function() { aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData); + delete aData.app.updateManifest; }); } @@ -1174,6 +1176,7 @@ this.DOMApplicationRegistry = { this._saveApps(function() { aData.app = app; + app.manifest = aManifest; if (!manifest.appcache_path) { aData.event = "downloadapplied"; aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData); @@ -1191,6 +1194,7 @@ this.DOMApplicationRegistry = { updateSvc.checkForUpdate(Services.io.newURI(aData.manifestURL, null, null), app.localId, false, updateObserver); } + delete app.manifest; }); // Update the permissions for this app. From 3d8ab473bcf58b22d773deb547ffb7f3eb3dfa25 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 20 Dec 2012 10:56:32 -0500 Subject: [PATCH 020/217] Bug 823117 - Don't shadow parameters with local variables. r=vchang --HG-- extra : rebase_source : d691831b627db2f2d1fd52341a72dff77e334a65 --- dom/wifi/WifiWorker.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 5b5f746dbb97..69fa7ccb2d36 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -2175,14 +2175,14 @@ WifiWorker.prototype = { var self = this; function getConnectionInformation() { - WifiManager.getConnectionInfo(function(info) { + WifiManager.getConnectionInfo(function(connInfo) { // See comments in calculateSignal for information about this. - if (!info) { + if (!connInfo) { self._lastConnectionInfo = null; return; } - let { rssi, linkspeed } = info; + let { rssi, linkspeed } = connInfo; if (rssi > 0) rssi -= 256; if (rssi <= MIN_RSSI) From 0e6cf3d853ec044b734d01bb49700e3d9b5301b6 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 17 Dec 2012 15:39:30 -0800 Subject: [PATCH 021/217] Bug 822494 - Protect against two calls to associate racing against each other to add the network. r=vchang --HG-- extra : rebase_source : d96448f5015a79d595ba09c0231b5c3880743d6c --- dom/wifi/WifiWorker.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 69fa7ccb2d36..fccccf69d5f1 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -1632,6 +1632,7 @@ function WifiWorker() { // all cases, the supplicant will take the last quotation that we pass it as // the end of the string. this.configuredNetworks = Object.create(null); + this._addingNetworks = Object.create(null); this.currentNetwork = null; this.ipAddress = ""; @@ -2646,6 +2647,11 @@ WifiWorker.prototype = { let networkKey = getNetworkKey(privnet); let configured; + if (networkKey in this._addingNetworks) { + this._sendMessage(message, false, "Racing associates"); + return; + } + if (networkKey in this.configuredNetworks) configured = this.configuredNetworks[networkKey]; @@ -2668,7 +2674,10 @@ WifiWorker.prototype = { // set it to being "enabled" before we add it and save the // configuration. privnet.disabled = 0; + this._addingNetworks[networkKey] = privnet; WifiManager.addNetwork(privnet, (function(ok) { + delete this._addingNetworks[networkKey]; + if (!ok) { this._sendMessage(message, false, "Network is misconfigured", msg); return; From 6d067dadfe05cd68d821e79011309ba6699b28ab Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Thu, 20 Dec 2012 12:11:31 -0800 Subject: [PATCH 022/217] Bug 708048 - Walk up tree to find a title for context menu items. r=kats --- mobile/android/chrome/content/browser.js | 81 ++++++++++++++++-------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 4c47075ec348..a5a32890562a 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1487,17 +1487,37 @@ var NativeWindow = { } }, + get _target() { + if (this._targetRef) + return this._targetRef.get(); + return null; + }, + + set _target(aTarget) { + if (aTarget) + this._targetRef = Cu.getWeakReference(aTarget); + else this._targetRef = null; + }, + _sendToContent: function(aX, aY) { - // initially we look for nearby clickable elements. If we don't find one we fall back to using whatever this click was on - let rootElement = ElementTouchHelper.elementFromPoint(aX, aY); - if (!rootElement) - rootElement = ElementTouchHelper.anyElementFromPoint(aX, aY) + // find and store the top most element this context menu is being shown for + // use the highlighted element if possible, otherwise look for nearby clickable elements + // If we still don't find one we fall back to using anything + let target = BrowserEventHandler._highlightElement || ElementTouchHelper.elementFromPoint(aX, aY); + if (!target) + target = ElementTouchHelper.anyElementFromPoint(aX, aY); + + if (!target) + return; + + // store a weakref to the target to be used when the context menu event returns + this._target = target; this.menuitems = {}; let menuitemsSet = false; - let element = rootElement; - if (!element) - return; + + // now walk up the tree and for each node look for any context menu items that apply + let element = target; while (element) { for each (let item in this.items) { @@ -1514,34 +1534,45 @@ var NativeWindow = { // only send the contextmenu event to content if we are planning to show a context menu (i.e. not on every long tap) if (menuitemsSet) { - let event = rootElement.ownerDocument.createEvent("MouseEvent"); + let event = target.ownerDocument.createEvent("MouseEvent"); event.initMouseEvent("contextmenu", true, true, content, 0, aX, aY, aX, aY, false, false, false, false, 0, null); - rootElement.ownerDocument.defaultView.addEventListener("contextmenu", this, false); - rootElement.dispatchEvent(event); - } else if (SelectionHandler.canSelect(rootElement)) { - SelectionHandler.startSelection(rootElement, aX, aY); + target.ownerDocument.defaultView.addEventListener("contextmenu", this, false); + target.dispatchEvent(event); + } else { + this._target = null; + BrowserEventHandler._cancelTapHighlight(); + + if (SelectionHandler.canSelect(rootElement)) + SelectionHandler.startSelection(rootElement, aX, aY); } }, _show: function(aEvent) { - if (aEvent.defaultPrevented) + let popupNode = this._target; + this._target = null; + if (aEvent.defaultPrevented || !popupNode) { return; + } Haptic.performSimpleAction(Haptic.LongPress); - let popupNode = aEvent.originalTarget; - let title = ""; - if (popupNode.hasAttribute("title")) { - title = popupNode.getAttribute("title") - } else if ((popupNode instanceof Ci.nsIDOMHTMLAnchorElement && popupNode.href) || - (popupNode instanceof Ci.nsIDOMHTMLAreaElement && popupNode.href)) { - title = this._getLinkURL(popupNode); - } else if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) { - title = popupNode.currentURI.spec; - } else if (popupNode instanceof Ci.nsIDOMHTMLMediaElement) { - title = (popupNode.currentSrc || popupNode.src); + // spin through the tree looking for a title for this context menu + let node = popupNode; + let title =""; + while(node && !title) { + if (node.hasAttribute("title")) { + title = node.getAttribute("title") + } else if ((node instanceof Ci.nsIDOMHTMLAnchorElement && node.href) || + (node instanceof Ci.nsIDOMHTMLAreaElement && node.href)) { + title = this._getLinkURL(node); + } else if (node instanceof Ci.nsIImageLoadingContent && node.currentURI) { + title = node.currentURI.spec; + } else if (node instanceof Ci.nsIDOMHTMLMediaElement) { + title = (node.currentSrc || node.src); + } + node = node.parentNode; } // convert this.menuitems object to an array for sending to native code @@ -1574,12 +1605,12 @@ var NativeWindow = { }, handleEvent: function(aEvent) { + BrowserEventHandler._cancelTapHighlight(); aEvent.target.ownerDocument.defaultView.removeEventListener("contextmenu", this, false); this._show(aEvent); }, observe: function(aSubject, aTopic, aData) { - BrowserEventHandler._cancelTapHighlight(); let data = JSON.parse(aData); // content gets first crack at cancelling context menus this._sendToContent(data.x, data.y); From 4ac27e83359c3d3f0f564625b8a47ebf30c15fb9 Mon Sep 17 00:00:00 2001 From: Asaf Romano Date: Thu, 20 Dec 2012 21:20:18 +0100 Subject: [PATCH 023/217] Bug 815352 (Part 2) - Add a stub view to implement in-content downloads. r=mak --- .../content/allDownloadsViewOverlay.js | 1 + .../content/allDownloadsViewOverlay.xul | 7 ++-- .../content/contentAreaDownloadsView.js | 10 ++++++ .../content/contentAreaDownloadsView.xul | 32 +++++++++++++++++++ browser/components/downloads/jar.mn | 2 ++ 5 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 browser/components/downloads/content/contentAreaDownloadsView.js create mode 100644 browser/components/downloads/content/contentAreaDownloadsView.xul diff --git a/browser/components/downloads/content/allDownloadsViewOverlay.js b/browser/components/downloads/content/allDownloadsViewOverlay.js index 90cf7cab0612..eb65f2ee8414 100644 --- a/browser/components/downloads/content/allDownloadsViewOverlay.js +++ b/browser/components/downloads/content/allDownloadsViewOverlay.js @@ -16,6 +16,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/DownloadUtils.jsm"); Cu.import("resource:///modules/DownloadsCommon.jsm"); +Cu.import("resource://gre/modules/PlacesUtils.jsm"); const nsIDM = Ci.nsIDownloadManager; diff --git a/browser/components/downloads/content/allDownloadsViewOverlay.xul b/browser/components/downloads/content/allDownloadsViewOverlay.xul index 63d0d4e3ac44..ee9d8a3dad11 100644 --- a/browser/components/downloads/content/allDownloadsViewOverlay.xul +++ b/browser/components/downloads/content/allDownloadsViewOverlay.xul @@ -21,13 +21,14 @@ - 3. Make sure your window also has the editMenuOverlay overlay applied, + 3. Make sure your window has the editMenuOverlay overlay applied, because the view implements cmd_copy and cmd_delete. - 4. To initialize the view + 4. Make sure your window has the globalOverlay.js script loaded. + 5. To initialize the view let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox")); // This is what the Places Library uses. It could be tweaked a bit as long as the // transition-type is set correctly - view.places = "place:transition=7&sort=4"; + view.place = "place:transition=7&sort=4"; --> + +# 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/. + + + + + + + + + + diff --git a/layout/reftests/svg/mask-type-04.svg b/layout/reftests/svg/mask-type-04.svg new file mode 100644 index 000000000000..ee46b5d57c35 --- /dev/null +++ b/layout/reftests/svg/mask-type-04.svg @@ -0,0 +1,16 @@ + + + + Testcase for alpha mask, dynamically setting mask-type + + + + + + + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index fd4a7368b6c2..cf9580147c81 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -177,6 +177,10 @@ fuzzy-if(Android,9,980) == gradient-live-01d.svg gradient-live-01-ref.svg == mask-extref-dataURI-01.svg pass.svg == mask-containing-masked-content-01.svg pass.svg == mask-transformed-01.svg mask-transformed-01-ref.svg +pref(layout.css.masking.enabled,true) == mask-type-01.svg mask-type-01-ref.svg +pref(layout.css.masking.enabled,true) == mask-type-02.svg mask-type-01-ref.svg +pref(layout.css.masking.enabled,true) == mask-type-03.svg mask-type-01-ref.svg +pref(layout.css.masking.enabled,true) == mask-type-04.svg mask-type-01-ref.svg == nested-viewBox-01.svg pass.svg == nesting-invalid-01.svg nesting-invalid-01-ref.svg == non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 5f7f90f23f58..2e542f2d24ee 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -164,6 +164,7 @@ CSS_KEY(activecaption, activecaption) CSS_KEY(alias, alias) CSS_KEY(all, all) CSS_KEY(all-scroll, all_scroll) +CSS_KEY(alpha, alpha) CSS_KEY(alternate, alternate) CSS_KEY(alternate-reverse, alternate_reverse) CSS_KEY(always, always) @@ -316,6 +317,7 @@ CSS_KEY(lower-latin, lower_latin) CSS_KEY(lower-roman, lower_roman) CSS_KEY(lowercase, lowercase) CSS_KEY(ltr, ltr) +CSS_KEY(luminance, luminance) CSS_KEY(manual, manual) CSS_KEY(margin-box, margin_box) CSS_KEY(matrix, matrix) diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 631f52f94824..58697964eeab 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -3294,6 +3294,16 @@ CSS_PROP_SVGRESET( nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) +CSS_PROP_SVGRESET( + mask-type, + mask_type, + MaskType, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.masking.enabled", + VARIANT_HK, + kMaskTypeKTable, + offsetof(nsStyleSVGReset, mMaskType), + eStyleAnimType_EnumU8) CSS_PROP_SVG( shape-rendering, shape_rendering, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 07d224fcc388..f2377ce3eec9 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -1586,6 +1586,12 @@ const int32_t nsCSSProps::kImageRenderingKTable[] = { eCSSKeyword_UNKNOWN, -1 }; +const int32_t nsCSSProps::kMaskTypeKTable[] = { + eCSSKeyword_luminance, NS_STYLE_MASK_TYPE_LUMINANCE, + eCSSKeyword_alpha, NS_STYLE_MASK_TYPE_ALPHA, + eCSSKeyword_UNKNOWN, -1 +}; + const int32_t nsCSSProps::kShapeRenderingKTable[] = { eCSSKeyword_auto, NS_STYLE_SHAPE_RENDERING_AUTO, eCSSKeyword_optimizespeed, NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 13550a24e6d9..b8c0109f54e5 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -406,6 +406,7 @@ public: static const int32_t kLineHeightKTable[]; static const int32_t kListStylePositionKTable[]; static const int32_t kListStyleKTable[]; + static const int32_t kMaskTypeKTable[]; static const int32_t kObjectOpacityKTable[]; static const int32_t kObjectPatternKTable[]; static const int32_t kOrientKTable[]; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 9a5c46358213..a258d4960948 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -4362,6 +4362,16 @@ nsComputedDOMStyle::DoGetMask() return val; } +CSSValue* +nsComputedDOMStyle::DoGetMaskType() +{ + nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue(); + val->SetIdent( + nsCSSProps::ValueToKeywordEnum(GetStyleSVGReset()->mMaskType, + nsCSSProps::kMaskTypeKTable)); + return val; +} + CSSValue* nsComputedDOMStyle::DoGetTransitionDelay() { @@ -4929,6 +4939,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(uint32_t* aLength) COMPUTED_STYLE_MAP_ENTRY(marker_mid, MarkerMid), COMPUTED_STYLE_MAP_ENTRY(marker_start, MarkerStart), COMPUTED_STYLE_MAP_ENTRY(mask, Mask), + COMPUTED_STYLE_MAP_ENTRY(mask_type, MaskType), COMPUTED_STYLE_MAP_ENTRY(shape_rendering, ShapeRendering), COMPUTED_STYLE_MAP_ENTRY(stop_color, StopColor), COMPUTED_STYLE_MAP_ENTRY(stop_opacity, StopOpacity), diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 93b2c20e8bc8..f223fd27a6e1 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -421,6 +421,7 @@ private: mozilla::dom::CSSValue* DoGetClipPath(); mozilla::dom::CSSValue* DoGetFilter(); mozilla::dom::CSSValue* DoGetMask(); + mozilla::dom::CSSValue* DoGetMaskType(); nsROCSSPrimitiveValue* GetROCSSPrimitiveValue(); nsDOMCSSValueList* GetROCSSValueList(bool aCommaDelimited); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index e43121e70247..1f1106358467 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -7617,6 +7617,13 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, svgReset->mMask = parentSVGReset->mMask; } + // mask-type: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForMaskType(), + svgReset->mMaskType, + canStoreInRuleTree, SETDSC_ENUMERATED, + parentSVGReset->mMaskType, + NS_STYLE_MASK_TYPE_LUMINANCE, 0, 0, 0, 0); + COMPUTE_END_RESET(SVGReset, svgReset) } diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index a26f90314431..ce1cfe45c0cc 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -970,6 +970,7 @@ nsStyleSVGReset::nsStyleSVGReset() mFloodOpacity = 1.0f; mDominantBaseline = NS_STYLE_DOMINANT_BASELINE_AUTO; mVectorEffect = NS_STYLE_VECTOR_EFFECT_NONE; + mMaskType = NS_STYLE_MASK_TYPE_LUMINANCE; } nsStyleSVGReset::~nsStyleSVGReset() @@ -990,6 +991,7 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) mFloodOpacity = aSource.mFloodOpacity; mDominantBaseline = aSource.mDominantBaseline; mVectorEffect = aSource.mVectorEffect; + mMaskType = aSource.mMaskType; } nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) const @@ -1010,7 +1012,8 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons mLightingColor != aOther.mLightingColor || mStopOpacity != aOther.mStopOpacity || mFloodOpacity != aOther.mFloodOpacity || - mVectorEffect != aOther.mVectorEffect) + mVectorEffect != aOther.mVectorEffect || + mMaskType != aOther.mMaskType) NS_UpdateHint(hint, nsChangeHint_RepaintFrame); return hint; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 8f07e600e4f7..9ee33a2ebf07 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2278,6 +2278,7 @@ struct nsStyleSVGReset { uint8_t mDominantBaseline; // [reset] see nsStyleConsts.h uint8_t mVectorEffect; // [reset] see nsStyleConsts.h + uint8_t mMaskType; // [reset] see nsStyleConsts.h }; #endif /* nsStyleStruct_h___ */ diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 13bc984d2424..3a95b2c56227 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -3578,6 +3578,14 @@ var gCSSProperties = { other_values: [ "url(#mymask)" ], invalid_values: [] }, + "mask-type": { + domProp: "maskType", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "luminance" ], + other_values: [ "alpha" ], + invalid_values: [] + }, "shape-rendering": { domProp: "shapeRendering", inherited: true, diff --git a/layout/svg/nsSVGMaskFrame.cpp b/layout/svg/nsSVGMaskFrame.cpp index 7764bfc8db38..c5113385de6b 100644 --- a/layout/svg/nsSVGMaskFrame.cpp +++ b/layout/svg/nsSVGMaskFrame.cpp @@ -119,20 +119,31 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsRenderingContext *aContext, nsSVGUtils::ConvertImageDataToLinearRGB(data, stride, rect); } - for (int32_t y = 0; y < surfaceSize.height; y++) - for (int32_t x = 0; x < surfaceSize.width; x++) { - uint8_t *pixel = data + stride * y + 4 * x; + if (GetStyleSVGReset()->mMaskType == NS_STYLE_MASK_TYPE_LUMINANCE) { + for (int32_t y = 0; y < surfaceSize.height; y++) { + for (int32_t x = 0; x < surfaceSize.width; x++) { + uint8_t *pixel = data + stride * y + 4 * x; - /* linearRGB -> intensity */ - uint8_t alpha = - static_cast - ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 + - pixel[GFX_ARGB32_OFFSET_G] * 0.7154 + - pixel[GFX_ARGB32_OFFSET_B] * 0.0721) * - (pixel[GFX_ARGB32_OFFSET_A] / 255.0) * aOpacity); + /* linearRGB -> intensity */ + uint8_t alpha = + static_cast + ((pixel[GFX_ARGB32_OFFSET_R] * 0.2125 + + pixel[GFX_ARGB32_OFFSET_G] * 0.7154 + + pixel[GFX_ARGB32_OFFSET_B] * 0.0721) * + (pixel[GFX_ARGB32_OFFSET_A] / 255.0) * aOpacity); - memset(pixel, alpha, 4); + memset(pixel, alpha, 4); + } } + } else { + for (int32_t y = 0; y < surfaceSize.height; y++) { + for (int32_t x = 0; x < surfaceSize.width; x++) { + uint8_t *pixel = data + stride * y + 4 * x; + uint8_t alpha = pixel[GFX_ARGB32_OFFSET_A] * aOpacity; + memset(pixel, alpha, 4); + } + } + } gfxPattern *retval = new gfxPattern(image); retval->SetMatrix(matrix); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index f7f1ddebcfcf..cd6f4a10a1ff 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1638,6 +1638,13 @@ pref("layout.css.dpi", -1); // of a CSS "px". This is only used for windows on the screen, not for printing. pref("layout.css.devPixelsPerPx", "-1.0"); +// Is support for CSS Masking features enabled? +#ifdef RELEASE_BUILD +pref("layout.css.masking.enabled", false); +#else +pref("layout.css.masking.enabled", true); +#endif + // Is support for the the @supports rule enabled? #ifdef RELEASE_BUILD pref("layout.css.supports-rule.enabled", false); From 7db62cbe18d7a4498e6b683aa8da3aa5e36b0669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Thu, 20 Dec 2012 20:04:58 -0500 Subject: [PATCH 055/217] Bug 823575 - Remove invalid argument. r=vladan. --- toolkit/content/aboutTelemetry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/content/aboutTelemetry.js b/toolkit/content/aboutTelemetry.js index 235db21b1b62..4251f2a5a0d7 100644 --- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -726,7 +726,7 @@ function onLoad() { Telemetry.asyncFetchTelemetryData(displayPingData); } -function displayPingData(ping) { +function displayPingData() { let ping = TelemetryPing.getPayload(); // Show simple measurements From 3a748a1b2768511bd78b997cf4e30589ad048a47 Mon Sep 17 00:00:00 2001 From: Frank Wein Date: Fri, 21 Dec 2012 02:25:26 +0100 Subject: [PATCH 056/217] Bug 819964 - Updatable plugin test should not hit the network, r=bsmedberg --- build/automation.py.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/automation.py.in b/build/automation.py.in index cb9475274106..18fa923eca22 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -450,6 +450,8 @@ user_pref("extensions.getAddons.get.url", "http://%(server)s/extensions-dummy/re user_pref("extensions.getAddons.getWithPerformance.url", "http://%(server)s/extensions-dummy/repositoryGetWithPerformanceURL"); user_pref("extensions.getAddons.search.browseURL", "http://%(server)s/extensions-dummy/repositoryBrowseURL"); user_pref("extensions.getAddons.search.url", "http://%(server)s/extensions-dummy/repositorySearchURL"); +// Make sure that opening the plugins check page won't hit the network +user_pref("plugins.update.url", "http://%(server)s/plugins-dummy/updateCheckURL"); // Make enablePrivilege continue to work for test code. :-( user_pref("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer", true); From d75825f8d90247be89caf0178837956186bde5d2 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 21 Dec 2012 12:26:32 +1100 Subject: [PATCH 057/217] Bug 798226 - allow the anchor arrow on a panel to move while the popup is open. r=enndeakin --- layout/xul/base/src/nsMenuPopupFrame.cpp | 21 +- toolkit/content/tests/widgets/Makefile.in | 1 + .../tests/widgets/test_popupanchor.xul | 271 ++++++++++++++++++ toolkit/content/widgets/popup.xml | 53 +++- 4 files changed, 334 insertions(+), 12 deletions(-) create mode 100644 toolkit/content/tests/widgets/test_popupanchor.xul diff --git a/layout/xul/base/src/nsMenuPopupFrame.cpp b/layout/xul/base/src/nsMenuPopupFrame.cpp index 36ad276839fd..37a7c0a9324c 100644 --- a/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -555,6 +555,8 @@ nsMenuPopupFrame::InitializePopup(nsIContent* aAnchorContent, mYPos = aYPos; mAdjustOffsetForContextMenu = false; mPosition = POPUPPOSITION_UNKNOWN; + mVFlip = false; + mHFlip = false; // if aAttributesOverride is true, then the popupanchor, popupalign and // position attributes on the override those values passed in. @@ -1183,12 +1185,19 @@ nsMenuPopupFrame::SetPopupPosition(nsIFrame* aAnchorFrame, bool aIsMove) } // mXPos and mYPos specify an additonal offset passed to OpenPopup that - // should be added to the position - if (IsDirectionRTL()) - screenPoint.x -= presContext->CSSPixelsToAppUnits(mXPos); - else - screenPoint.x += presContext->CSSPixelsToAppUnits(mXPos); - screenPoint.y += presContext->CSSPixelsToAppUnits(mYPos); + // should be added to the position. We also add the offset to the anchor + // pos so a later flip/resize takes the offset into account. + nscoord anchorXOffset = presContext->CSSPixelsToAppUnits(mXPos); + if (IsDirectionRTL()) { + screenPoint.x -= anchorXOffset; + anchorRect.x -= anchorXOffset; + } else { + screenPoint.x += anchorXOffset; + anchorRect.x += anchorXOffset; + } + nscoord anchorYOffset = presContext->CSSPixelsToAppUnits(mYPos); + screenPoint.y += anchorYOffset; + anchorRect.y += anchorYOffset; // If this is a noautohide popup, set the screen coordinates of the popup. // This way, the popup stays at the location where it was opened even when diff --git a/toolkit/content/tests/widgets/Makefile.in b/toolkit/content/tests/widgets/Makefile.in index db86219a05e7..f6028ebccab0 100644 --- a/toolkit/content/tests/widgets/Makefile.in +++ b/toolkit/content/tests/widgets/Makefile.in @@ -21,6 +21,7 @@ MOCHITEST_FILES = \ test_tree_column_reorder.xul \ tree_shared.js \ test_mousecapture_area.html \ + test_popupanchor.xul \ popup_shared.js \ test_videocontrols.html \ test_videocontrols_video_direction.html \ diff --git a/toolkit/content/tests/widgets/test_popupanchor.xul b/toolkit/content/tests/widgets/test_popupanchor.xul new file mode 100644 index 000000000000..e8cab7f6f9e8 --- /dev/null +++ b/toolkit/content/tests/widgets/test_popupanchor.xul @@ -0,0 +1,271 @@ + + + + + + + Popup Popup Tests + + + + + + + + + + +
+

The anchor --> v <--

+
+ + +
diff --git a/toolkit/content/widgets/popup.xml b/toolkit/content/widgets/popup.xml index a82501b9c695..456f2c9ac801 100644 --- a/toolkit/content/widgets/popup.xml +++ b/toolkit/content/widgets/popup.xml @@ -340,10 +340,45 @@ null - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - From 3c55f766f6ec820e4ae808c998930a94bdca97af Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Thu, 20 Dec 2012 17:37:56 -0800 Subject: [PATCH 058/217] Bug 699859 - Create nsIContentPrefService2, an async version of nsIContentPrefService. r=mak, sr=mossop --- dom/interfaces/base/Makefile.in | 1 + .../base/nsIContentPrefService2.idl | 365 ++++++++ testing/xpcshell/xpcshell.ini | 1 + .../contentprefs/ContentPrefService2.jsm | 798 ++++++++++++++++++ .../contentprefs/ContentPrefStore.jsm | 115 +++ toolkit/components/contentprefs/Makefile.in | 11 +- .../contentprefs/nsContentPrefService.js | 230 ++--- .../components/contentprefs/tests/Makefile.in | 5 +- .../tests/unit_cps2/AsyncRunner.jsm | 69 ++ .../contentprefs/tests/unit_cps2/head.js | 311 +++++++ .../tests/unit_cps2/test_getCached.js | 99 +++ .../unit_cps2/test_getCachedSubdomains.js | 190 +++++ .../tests/unit_cps2/test_getSubdomains.js | 73 ++ .../tests/unit_cps2/test_observers.js | 125 +++ .../tests/unit_cps2/test_remove.js | 194 +++++ .../tests/unit_cps2/test_removeAllDomains.js | 73 ++ .../tests/unit_cps2/test_removeByDomain.js | 162 ++++ .../tests/unit_cps2/test_removeByName.js | 85 ++ .../tests/unit_cps2/test_service.js | 12 + .../tests/unit_cps2/test_setGet.js | 138 +++ .../contentprefs/tests/unit_cps2/xpcshell.ini | 14 + 21 files changed, 2906 insertions(+), 165 deletions(-) create mode 100644 dom/interfaces/base/nsIContentPrefService2.idl create mode 100644 toolkit/components/contentprefs/ContentPrefService2.jsm create mode 100644 toolkit/components/contentprefs/ContentPrefStore.jsm create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.jsm create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/head.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_observers.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_remove.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_service.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js create mode 100644 toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini diff --git a/dom/interfaces/base/Makefile.in b/dom/interfaces/base/Makefile.in index 64ebda37f076..6009821d2946 100644 --- a/dom/interfaces/base/Makefile.in +++ b/dom/interfaces/base/Makefile.in @@ -27,6 +27,7 @@ XPIDLSRCS = \ nsIBrowserDOMWindow.idl \ nsIContentPermissionPrompt.idl \ nsIContentPrefService.idl \ + nsIContentPrefService2.idl \ nsIContentURIGrouper.idl \ nsIDOMClientInformation.idl \ nsIDOMConstructor.idl \ diff --git a/dom/interfaces/base/nsIContentPrefService2.idl b/dom/interfaces/base/nsIContentPrefService2.idl new file mode 100644 index 000000000000..4ff2434e4af7 --- /dev/null +++ b/dom/interfaces/base/nsIContentPrefService2.idl @@ -0,0 +1,365 @@ +/* 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/. */ + +#include "nsISupports.idl" + +interface nsIVariant; +interface nsIContentPrefObserver; +interface nsIContentPrefCallback2; +interface nsILoadContext; +interface nsIContentPref; + +/** + * Content Preferences + * + * Content preferences allow the application to associate arbitrary data, or + * "preferences", with specific domains, or web "content". Specifically, a + * content preference is a structure with three values: a domain with which the + * preference is associated, a name that identifies the preference within its + * domain, and a value. (See nsIContentPref below.) + * + * For example, if you want to remember the user's preference for a certain zoom + * level on www.mozilla.org pages, you might store a preference whose domain is + * "www.mozilla.org", whose name is "zoomLevel", and whose value is the numeric + * zoom level. + * + * A preference need not have a domain, and in that case the preference is + * called a "global" preference. This interface doesn't impart any special + * significance to global preferences; they're simply name-value pairs that + * aren't associated with any particular domain. As a consumer of this + * interface, you might choose to let a global preference override all non- + * global preferences of the same name, for example, for whatever definition of + * "override" is appropriate for your use case. + * + * + * Domain Parameters + * + * Many methods of this interface accept a "domain" parameter. Domains may be + * specified either exactly, like "example.com", or as full URLs, like + * "http://example.com/foo/bar". In the latter case the API extracts the full + * domain from the URL, so if you specify "http://foo.bar.example.com/baz", the + * domain is taken to be "foo.bar.example.com", not "example.com". + * + * + * Private-Browsing Context Parameters + * + * Many methods also accept a "context" parameter. This parameter relates to + * private browsing and determines the kind of storage that a method uses, + * either the usual permanent storage or temporary storage set aside for private + * browsing sessions. + * + * Pass null to unconditionally use permanent storage. Pass an nsILoadContext + * to use storage appropriate to the context's usePrivateBrowsing attribute: if + * usePrivateBrowsing is true, temporary private-browsing storage is used, and + * otherwise permanent storage is used. A context can be obtained from the + * window or channel whose content pertains to the preferences being modified or + * retrieved. + * + * + * Callbacks + * + * The methods of callback objects are always called asynchronously. See + * nsIContentPrefCallback2 below for more information about callbacks. + */ + +[scriptable, uuid(51e1d34a-5e9d-4b77-b14c-0f8346e264ca)] +interface nsIContentPrefService2 : nsISupports +{ + /** + * Gets the preference with the given domain and name. + * + * @param domain The preference's domain. + * @param name The preference's name. + * @param context The private-browsing context, if any. + * @param callback handleResult is called once unless no such preference + * exists, in which case handleResult is not called at all. + */ + void getByDomainAndName(in AString domain, + in AString name, + in nsILoadContext context, + in nsIContentPrefCallback2 callback); + + /** + * Gets all preferences with the given name whose domains are either the same + * as or subdomains of the given domain. + * + * @param domain The preferences' domain. + * @param name The preferences' name. + * @param context The private-browsing context, if any. + * @param callback handleResult is called once for each preference. If no + * such preferences exist, handleResult is not called at all. + */ + void getBySubdomainAndName(in AString domain, + in AString name, + in nsILoadContext context, + in nsIContentPrefCallback2 callback); + + /** + * Gets the preference with no domain and the given name. + * + * @param name The preference's name. + * @param context The private-browsing context, if any. + * @param callback handleResult is called once unless no such preference + * exists, in which case handleResult is not called at all. + */ + void getGlobal(in AString name, + in nsILoadContext context, + in nsIContentPrefCallback2 callback); + + /** + * Synchronously retrieves from the in-memory cache the preference with the + * given domain and name. + * + * In addition to caching preference values, the cache also keeps track of + * preferences that are known not to exist. If the preference is known not to + * exist, the value attribute of the returned object will be undefined + * (nsIDataType::VTYPE_VOID). + * + * If the preference is neither cached nor known not to exist, then null is + * returned, and get() must be called to determine whether the preference + * exists. + * + * @param domain The preference's domain. + * @param name The preference's name. + * @param context The private-browsing context, if any. + * @return The preference, or null if no such preference is known to + * exist. + */ + nsIContentPref getCachedByDomainAndName(in AString domain, + in AString name, + in nsILoadContext context); + + /** + * Synchronously retrieves from the in-memory cache all preferences with the + * given name whose domains are either the same as or subdomains of the given + * domain. + * + * The preferences are returned in an array through the out-parameter. If a + * preference for a particular subdomain is known not to exist, then an object + * corresponding to that preference will be present in the array, and, as with + * getCachedByDomainAndName, its value attribute will be undefined. + * + * @param domain The preferences' domain. + * @param name The preferences' name. + * @param context The private-browsing context, if any. + * @param len The length of the returned array. + * @param prefs The array of preferences. + */ + void getCachedBySubdomainAndName(in AString domain, + in AString name, + in nsILoadContext context, + out unsigned long len, + [retval,array,size_is(len)] out nsIContentPref prefs); + + /** + * Synchronously retrieves from the in-memory cache the preference with no + * domain and the given name. + * + * As with getCachedByDomainAndName, if the preference is cached then it is + * returned; if the preference is known not to exist, then the value attribute + * of the returned object will be undefined; if the preference is neither + * cached nor known not to exist, then null is returned. + * + * @param name The preference's name. + * @param context The private-browsing context, if any. + * @return The preference, or null if no such preference is known to + * exist. + */ + nsIContentPref getCachedGlobal(in AString name, + in nsILoadContext context); + + /** + * Sets a preference. + * + * @param domain The preference's domain. + * @param name The preference's name. + * @param value The preference's value. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the preference has been + * stored. + */ + void set(in AString domain, + in AString name, + in nsIVariant value, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Sets a preference with no domain. + * + * @param name The preference's name. + * @param value The preference's value. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the preference has been + * stored. + */ + void setGlobal(in AString name, + in nsIVariant value, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes the preference with the given domain and name. + * + * @param domain The preference's domain. + * @param name The preference's name. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeByDomainAndName(in AString domain, + in AString name, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes all the preferences with the given name whose domains are either + * the same as or subdomains of the given domain. + * + * @param domain The preferences' domain. + * @param name The preferences' name. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeBySubdomainAndName(in AString domain, + in AString name, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes the preference with no domain and the given name. + * + * @param name The preference's name. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeGlobal(in AString name, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes all preferences with the given domain. + * + * @param domain The preferences' domain. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeByDomain(in AString domain, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes all preferences whose domains are either the same as or subdomains + * of the given domain. + * + * @param domain The preferences' domain. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeBySubdomain(in AString domain, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes all preferences with the given name regardless of domain, including + * global preferences with the given name. + * + * @param name The preferences' name. + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeByName(in AString name, + in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes all non-global preferences -- in other words, all preferences that + * have a domain. + * + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeAllDomains(in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Removes all global preferences -- in other words, all preferences that have + * no domain. + * + * @param context The private-browsing context, if any. + * @param callback handleCompletion is called when the operation completes. + */ + void removeAllGlobals(in nsILoadContext context, + [optional] in nsIContentPrefCallback2 callback); + + /** + * Registers an observer that will be notified whenever a preference with the + * given name is set or removed. + * + * When a set or remove method is called, observers are notified after the set + * or removal completes but before method's callback is called. + * + * The service holds a strong reference to the observer, so the observer must + * be removed later to avoid leaking it. + * + * @param name The name of the preferences to observe. Pass null to + * observe all preference changes regardless of name. + * @param observer The observer. + */ + void addObserverForName(in AString name, + in nsIContentPrefObserver observer); + + /** + * Unregisters an observer for the given name. + * + * @param name The name for which the observer was registered. Pass null + * if the observer was added with a null name. + * @param observer The observer. + */ + void removeObserverForName(in AString name, + in nsIContentPrefObserver observer); +}; + +/** + * The callback used by the above methods. + */ +[scriptable, uuid(1a12cf41-79e8-4d0f-9899-2f7b27c5d9a1)] +interface nsIContentPrefCallback2 : nsISupports +{ + /** + * For the retrieval methods, this is called once for each retrieved + * preference. It is not called for other methods. + * + * @param pref The retrieved preference. + */ + void handleResult(in nsIContentPref pref); + + /** + * Called when an error occurs. This may be called multiple times before + * onComplete is called. + * + * @param error A number in Components.results describing the error. + */ + void handleError(in nsresult error); + + /** + * Called when the method finishes. This will be called exactly once for + * each method invocation, and afterward no other callback methods will be + * called. + * + * @param reason One of the COMPLETE_* values indicating the manner in which + * the method completed. + */ + void handleCompletion(in unsigned short reason); + + const unsigned short COMPLETE_OK = 0; + const unsigned short COMPLETE_ERROR = 1; +}; + +[scriptable, function, uuid(9f24948d-24b5-4b1b-b554-7dbd58c1d792)] +interface nsIContentPref : nsISupports +{ + readonly attribute AString domain; + readonly attribute AString name; + readonly attribute nsIVariant value; +}; diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index 97745f4fb71b..701934d847e1 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -26,6 +26,7 @@ [include:embedding/tests/unit/xpcshell.ini] [include:toolkit/components/commandlines/test/unit/xpcshell.ini] [include:toolkit/components/contentprefs/tests/unit/xpcshell.ini] +[include:toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini] [include:toolkit/devtools/debugger/tests/unit/xpcshell.ini] [include:toolkit/devtools/sourcemap/tests/unit/xpcshell.ini] [include:toolkit/components/passwordmgr/test/unit/xpcshell.ini] diff --git a/toolkit/components/contentprefs/ContentPrefService2.jsm b/toolkit/components/contentprefs/ContentPrefService2.jsm new file mode 100644 index 000000000000..95abe4171969 --- /dev/null +++ b/toolkit/components/contentprefs/ContentPrefService2.jsm @@ -0,0 +1,798 @@ +/* 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/. */ + +// This file is an XPCOM component that implements nsIContentPrefService2. +// Although it's a JSM, it's not intended to be imported by consumers like JSMs +// are usually imported. It's only a JSM so that nsContentPrefService.js can +// easily use it. Consumers should access this component with the usual XPCOM +// rigmarole: +// +// Cc["@mozilla.org/content-pref/service;1"]. +// getService(Ci.nsIContentPrefService2); +// +// That contract ID actually belongs to nsContentPrefService.js, which, when +// QI'ed to nsIContentPrefService2, returns an instance of this component. +// +// The plan is to eventually remove nsIContentPrefService and its +// implementation, nsContentPrefService.js. At such time this file can stop +// being a JSM, and the "_cps" parts that ContentPrefService2 relies on and +// NSGetFactory and all the other XPCOM initialization goop in +// nsContentPrefService.js can be moved here. +// +// See https://bugzilla.mozilla.org/show_bug.cgi?id=699859 + +let EXPORTED_SYMBOLS = [ + "ContentPrefService2", +]; + +const { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/ContentPrefStore.jsm"); + +function ContentPrefService2(cps) { + this._cps = cps; + this._cache = cps._cache; + this._pbStore = cps._privModeStorage; +} + +ContentPrefService2.prototype = { + + getByDomainAndName: function CPS2_getByDomainAndName(group, name, context, + callback) { + checkGroupArg(group); + this._get(group, name, false, context, callback); + }, + + getBySubdomainAndName: function CPS2_getBySubdomainAndName(group, name, + context, + callback) { + checkGroupArg(group); + this._get(group, name, true, context, callback); + }, + + getGlobal: function CPS2_getGlobal(name, context, callback) { + this._get(null, name, false, context, callback); + }, + + _get: function CPS2__get(group, name, includeSubdomains, context, callback) { + group = this._parseGroup(group); + checkNameArg(name); + checkCallbackArg(callback, true); + + // Some prefs may be in both the database and the private browsing store. + // Notify the caller of such prefs only once, using the values from private + // browsing. + let pbPrefs = new ContentPrefStore(); + if (context && context.usePrivateBrowsing) { + for (let [sgroup, val] in + this._pbStore.match(group, name, includeSubdomains)) { + pbPrefs.set(sgroup, name, val); + } + } + + this._execStmts([this._commonGetStmt(group, name, includeSubdomains)], { + onRow: function onRow(row) { + let grp = row.getResultByName("grp"); + let val = row.getResultByName("value"); + this._cache.set(grp, name, val); + if (!pbPrefs.has(group, name)) + cbHandleResult(callback, new ContentPref(grp, name, val)); + }, + onDone: function onDone(reason, ok, gotRow) { + if (ok) { + if (!gotRow) + this._cache.set(group, name, undefined); + for (let [pbGroup, pbName, pbVal] in pbPrefs) { + cbHandleResult(callback, new ContentPref(pbGroup, pbName, pbVal)); + } + } + cbHandleCompletion(callback, reason); + }, + onError: function onError(nsresult) { + cbHandleError(callback, nsresult); + } + }); + }, + + _commonGetStmt: function CPS2__commonGetStmt(group, name, includeSubdomains) { + let stmt = group ? + this._stmtWithGroupClause(group, includeSubdomains, + "SELECT groups.name AS grp, prefs.value AS value", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "JOIN groups ON groups.id = prefs.groupID", + "WHERE settings.name = :name AND prefs.groupID IN ($)" + ) : + this._stmt( + "SELECT NULL AS grp, prefs.value AS value", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "WHERE settings.name = :name AND prefs.groupID ISNULL" + ); + stmt.params.name = name; + return stmt; + }, + + _stmtWithGroupClause: function CPS2__stmtWithGroupClause(group, + includeSubdomains) { + let stmt = this._stmt(joinArgs(Array.slice(arguments, 2)).replace("$", + "SELECT id " + + "FROM groups " + + "WHERE name = :group OR " + + "(:includeSubdomains AND name LIKE :pattern ESCAPE '/')" + )); + stmt.params.group = group; + stmt.params.includeSubdomains = includeSubdomains || false; + stmt.params.pattern = "%." + stmt.escapeStringForLIKE(group, "/"); + return stmt; + }, + + getCachedByDomainAndName: function CPS2_getCachedByDomainAndName(group, + name, + context) { + checkGroupArg(group); + let prefs = this._getCached(group, name, false, context); + return prefs[0] || null; + }, + + getCachedBySubdomainAndName: function CPS2_getCachedBySubdomainAndName(group, + name, + context, + len) { + checkGroupArg(group); + let prefs = this._getCached(group, name, true, context); + if (len) + len.value = prefs.length; + return prefs; + }, + + getCachedGlobal: function CPS2_getCachedGlobal(name, context) { + let prefs = this._getCached(null, name, false, context); + return prefs[0] || null; + }, + + _getCached: function CPS2__getCached(group, name, includeSubdomains, + context) { + group = this._parseGroup(group); + checkNameArg(name); + + let storesToCheck = [this._cache]; + if (context && context.usePrivateBrowsing) + storesToCheck.push(this._pbStore); + + let outStore = new ContentPrefStore(); + storesToCheck.forEach(function (store) { + for (let [sgroup, val] in store.match(group, name, includeSubdomains)) { + outStore.set(sgroup, name, val); + } + }); + + let prefs = []; + for (let [sgroup, sname, val] in outStore) { + prefs.push(new ContentPref(sgroup, sname, val)); + } + return prefs; + }, + + set: function CPS2_set(group, name, value, context, callback) { + checkGroupArg(group); + this._set(group, name, value, context, callback); + }, + + setGlobal: function CPS2_setGlobal(name, value, context, callback) { + this._set(null, name, value, context, callback); + }, + + _set: function CPS2__set(group, name, value, context, callback) { + group = this._parseGroup(group); + checkNameArg(name); + checkValueArg(value); + checkCallbackArg(callback, false); + + if (context && context.usePrivateBrowsing) { + this._pbStore.set(group, name, value); + this._schedule(function () { + this._cps._notifyPrefSet(group, name, value); + cbHandleCompletion(callback, Ci.nsIContentPrefCallback2.COMPLETE_OK); + }); + return; + } + + let stmts = []; + + // Create the setting if it doesn't exist. + let stmt = this._stmt( + "INSERT OR IGNORE INTO settings (id, name)", + "VALUES((SELECT id FROM settings WHERE name = :name), :name)" + ); + stmt.params.name = name; + stmts.push(stmt); + + // Create the group if it doesn't exist. + if (group) { + stmt = this._stmt( + "INSERT OR IGNORE INTO groups (id, name)", + "VALUES((SELECT id FROM groups WHERE name = :group), :group)" + ); + stmt.params.group = group; + stmts.push(stmt); + } + + // Finally create or update the pref. + if (group) { + stmt = this._stmt( + "INSERT OR REPLACE INTO prefs (id, groupID, settingID, value)", + "VALUES(", + "(SELECT prefs.id", + "FROM prefs", + "JOIN groups ON groups.id = prefs.groupID", + "JOIN settings ON settings.id = prefs.settingID", + "WHERE groups.name = :group AND settings.name = :name),", + "(SELECT id FROM groups WHERE name = :group),", + "(SELECT id FROM settings WHERE name = :name),", + ":value", + ")" + ); + stmt.params.group = group; + } + else { + stmt = this._stmt( + "INSERT OR REPLACE INTO prefs (id, groupID, settingID, value)", + "VALUES(", + "(SELECT prefs.id", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "WHERE prefs.groupID IS NULL AND settings.name = :name),", + "NULL,", + "(SELECT id FROM settings WHERE name = :name),", + ":value", + ")" + ); + } + stmt.params.name = name; + stmt.params.value = value; + stmts.push(stmt); + + this._execStmts(stmts, { + onDone: function onDone(reason, ok) { + if (ok) { + this._cache.setWithCast(group, name, value); + this._cps._notifyPrefSet(group, name, value); + } + cbHandleCompletion(callback, reason); + }, + onError: function onError(nsresult) { + cbHandleError(callback, nsresult); + } + }); + }, + + removeByDomainAndName: function CPS2_removeByDomainAndName(group, name, + context, + callback) { + checkGroupArg(group); + this._remove(group, name, false, context, callback); + }, + + removeBySubdomainAndName: function CPS2_removeBySubdomainAndName(group, name, + context, + callback) { + checkGroupArg(group); + this._remove(group, name, true, context, callback); + }, + + removeGlobal: function CPS2_removeGlobal(name, context,callback) { + this._remove(null, name, false, context, callback); + }, + + _remove: function CPS2__remove(group, name, includeSubdomains, context, + callback) { + group = this._parseGroup(group); + checkNameArg(name); + checkCallbackArg(callback, false); + + let stmts = []; + + // First get the matching prefs. + stmts.push(this._commonGetStmt(group, name, includeSubdomains)); + + // Delete the matching prefs. + let stmt = this._stmtWithGroupClause(group, includeSubdomains, + "DELETE FROM prefs", + "WHERE settingID = (SELECT id FROM settings WHERE name = :name) AND", + "CASE typeof(:group)", + "WHEN 'null' THEN prefs.groupID IS NULL", + "ELSE prefs.groupID IN ($)", + "END" + ); + stmt.params.name = name; + stmts.push(stmt); + + // Delete settings and groups that are no longer used. The NOTNULL term in + // the subquery of the second statment is needed because of SQLite's weird + // IN behavior vis-a-vis NULLs. See http://sqlite.org/lang_expr.html. + stmts.push(this._stmt( + "DELETE FROM settings", + "WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)" + )); + stmts.push(this._stmt( + "DELETE FROM groups WHERE id NOT IN (", + "SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL", + ")" + )); + + let prefs = new ContentPrefStore(); + + this._execStmts(stmts, { + onRow: function onRow(row) { + let grp = row.getResultByName("grp"); + prefs.set(grp, name); + this._cache.set(grp, name, undefined); + }, + onDone: function onDone(reason, ok) { + if (ok) { + this._cache.set(group, name, undefined); + if (context && context.usePrivateBrowsing) { + for (let [sgroup, ] in + this._pbStore.match(group, name, includeSubdomains)) { + prefs.set(sgroup, name); + this._pbStore.remove(sgroup, name); + } + } + for (let [sgroup, , ] in prefs) { + this._cps._notifyPrefRemoved(sgroup, name); + } + } + cbHandleCompletion(callback, reason); + }, + onError: function onError(nsresult) { + cbHandleError(callback, nsresult); + } + }); + }, + + removeByDomain: function CPS2_removeByDomain(group, context, callback) { + checkGroupArg(group); + this._removeByDomain(group, false, context, callback); + }, + + removeBySubdomain: function CPS2_removeBySubdomain(group, context, callback) { + checkGroupArg(group); + this._removeByDomain(group, true, context, callback); + }, + + removeAllGlobals: function CPS2_removeAllGlobals(context, callback) { + this._removeByDomain(null, false, context, callback); + }, + + _removeByDomain: function CPS2__removeByDomain(group, includeSubdomains, + context, callback) { + group = this._parseGroup(group); + checkCallbackArg(callback, false); + + let stmts = []; + + // First get the matching prefs, then delete groups and prefs that reference + // deleted groups. + if (group) { + stmts.push(this._stmtWithGroupClause(group, includeSubdomains, + "SELECT groups.name AS grp, settings.name AS name", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "JOIN groups ON groups.id = prefs.groupID", + "WHERE prefs.groupID IN ($)" + )); + stmts.push(this._stmtWithGroupClause(group, includeSubdomains, + "DELETE FROM groups WHERE id IN ($)" + )); + stmts.push(this._stmt( + "DELETE FROM prefs", + "WHERE groupID NOTNULL AND groupID NOT IN (SELECT id FROM groups)" + )); + } + else { + stmts.push(this._stmt( + "SELECT NULL AS grp, settings.name AS name", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "WHERE prefs.groupID IS NULL" + )); + stmts.push(this._stmt( + "DELETE FROM prefs WHERE groupID IS NULL" + )); + } + + // Finally delete settings that are no longer referenced. + stmts.push(this._stmt( + "DELETE FROM settings", + "WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)" + )); + + let prefs = new ContentPrefStore(); + + this._execStmts(stmts, { + onRow: function onRow(row) { + let grp = row.getResultByName("grp"); + let name = row.getResultByName("name"); + prefs.set(grp, name); + this._cache.set(grp, name, undefined); + }, + onDone: function onDone(reason, ok) { + if (ok) { + if (context && context.usePrivateBrowsing) { + for (let [sgroup, sname, ] in this._pbStore) { + prefs.set(sgroup, sname); + this._pbStore.remove(sgroup, sname); + } + } + for (let [sgroup, sname, ] in prefs) { + this._cps._notifyPrefRemoved(sgroup, sname); + } + } + cbHandleCompletion(callback, reason); + }, + onError: function onError(nsresult) { + cbHandleError(callback, nsresult); + } + }); + }, + + removeAllDomains: function CPS2_removeAllDomains(context, callback) { + checkCallbackArg(callback, false); + let stmts = []; + + // First get the matching prefs. + stmts.push(this._stmt( + "SELECT groups.name AS grp, settings.name AS name", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "JOIN groups ON groups.id = prefs.groupID" + )); + + stmts.push(this._stmt( + "DELETE FROM prefs WHERE groupID NOTNULL" + )); + stmts.push(this._stmt( + "DELETE FROM groups" + )); + stmts.push(this._stmt( + "DELETE FROM settings", + "WHERE id NOT IN (SELECT DISTINCT settingID FROM prefs)" + )); + + let prefs = new ContentPrefStore(); + + this._execStmts(stmts, { + onRow: function onRow(row) { + let grp = row.getResultByName("grp"); + let name = row.getResultByName("name"); + prefs.set(grp, name); + this._cache.set(grp, name, undefined); + }, + onDone: function onDone(reason, ok) { + if (ok) { + if (context && context.usePrivateBrowsing) { + for (let [sgroup, sname, ] in this._pbStore) { + prefs.set(sgroup, sname); + } + this._pbStore.removeGrouped(); + } + for (let [sgroup, sname, ] in prefs) { + this._cps._notifyPrefRemoved(sgroup, sname); + } + } + cbHandleCompletion(callback, reason); + }, + onError: function onError(nsresult) { + cbHandleError(callback, nsresult); + } + }); + }, + + removeByName: function CPS2_removeByName(name, context, callback) { + checkNameArg(name); + checkCallbackArg(callback, false); + + let stmts = []; + + // First get the matching prefs. Include null if any of those prefs are + // global. + let stmt = this._stmt( + "SELECT groups.name AS grp", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "JOIN groups ON groups.id = prefs.groupID", + "WHERE settings.name = :name", + "UNION", + "SELECT NULL AS grp", + "WHERE EXISTS (", + "SELECT prefs.id", + "FROM prefs", + "JOIN settings ON settings.id = prefs.settingID", + "WHERE settings.name = :name AND prefs.groupID IS NULL", + ")" + ); + stmt.params.name = name; + stmts.push(stmt); + + // Delete the target settings. + stmt = this._stmt( + "DELETE FROM settings WHERE name = :name" + ); + stmt.params.name = name; + stmts.push(stmt); + + // Delete prefs and groups that are no longer used. + stmts.push(this._stmt( + "DELETE FROM prefs WHERE settingID NOT IN (SELECT id FROM settings)" + )); + stmts.push(this._stmt( + "DELETE FROM groups WHERE id NOT IN (", + "SELECT DISTINCT groupID FROM prefs WHERE groupID NOTNULL", + ")" + )); + + let prefs = new ContentPrefStore(); + + this._execStmts(stmts, { + onRow: function onRow(row) { + let grp = row.getResultByName("grp"); + prefs.set(grp, name); + this._cache.set(grp, name, undefined); + }, + onDone: function onDone(reason, ok) { + if (ok) { + if (context && context.usePrivateBrowsing) { + for (let [sgroup, sname, ] in this._pbStore) { + if (sname === name) { + prefs.set(sgroup, name); + this._pbStore.remove(sgroup, name); + } + } + } + for (let [sgroup, , ] in prefs) { + this._cps._notifyPrefRemoved(sgroup, name); + } + } + cbHandleCompletion(callback, reason); + }, + onError: function onError(nsresult) { + cbHandleError(callback, nsresult); + } + }); + }, + + destroy: function CPS2_destroy() { + for each (let stmt in this._statements) { + stmt.finalize(); + } + }, + + /** + * Returns the cached mozIStorageAsyncStatement for the given SQL. If no such + * statement is cached, one is created and cached. + * + * @param sql The SQL query string. If more than one string is given, then + * all are concatenated. The concatenation process inserts + * spaces where appropriate and removes unnecessary contiguous + * spaces. Call like _stmt("SELECT *", "FROM foo"). + * @return The cached, possibly new, statement. + */ + _stmt: function CPS2__stmt(sql /*, sql2, sql3, ... */) { + let sql = joinArgs(arguments); + if (!this._statements) + this._statements = {}; + if (!this._statements[sql]) + this._statements[sql] = this._cps._dbConnection.createAsyncStatement(sql); + return this._statements[sql]; + }, + + /** + * Executes some async statements. + * + * @param stmts An array of mozIStorageAsyncStatements. + * @param callbacks An object with the following methods: + * onRow(row) (optional) + * Called once for each result row. + * row: A mozIStorageRow. + * onDone(reason, reasonOK, didGetRow) (required) + * Called when done. + * reason: A nsIContentPrefService2.COMPLETE_* value. + * reasonOK: reason == nsIContentPrefService2.COMPLETE_OK. + * didGetRow: True if onRow was ever called. + * onError(nsresult) (optional) + * Called on error. + * nsresult: The error code. + */ + _execStmts: function CPS2__execStmts(stmts, callbacks) { + let self = this; + let gotRow = false; + this._cps._dbConnection.executeAsync(stmts, stmts.length, { + handleResult: function handleResult(results) { + try { + let row = null; + while ((row = results.getNextRow())) { + gotRow = true; + if (callbacks.onRow) + callbacks.onRow.call(self, row); + } + } + catch (err) { + Cu.reportError(err); + } + }, + handleCompletion: function handleCompletion(reason) { + try { + let ok = reason == Ci.mozIStorageStatementCallback.REASON_FINISHED; + callbacks.onDone.call(self, + ok ? Ci.nsIContentPrefCallback2.COMPLETE_OK : + Ci.nsIContentPrefCallback2.COMPLETE_ERROR, + ok, gotRow); + } + catch (err) { + Cu.reportError(err); + } + }, + handleError: function handleError(error) { + try { + if (callbacks.onError) + callbacks.onError.call(self, Cr.NS_ERROR_FAILURE); + } + catch (err) { + Cu.reportError(err); + } + } + }); + }, + + /** + * Parses the domain (the "group", to use the database's term) from the given + * string. + * + * @param groupStr Assumed to be either a string or falsey. + * @return If groupStr is a valid URL string, returns the domain of + * that URL. If groupStr is some other nonempty string, + * returns groupStr itself. Otherwise returns null. + */ + _parseGroup: function CPS2__parseGroup(groupStr) { + if (!groupStr) + return null; + try { + var groupURI = Services.io.newURI(groupStr, null, null); + } + catch (err) { + return groupStr; + } + return this._cps.grouper.group(groupURI); + }, + + _schedule: function CPS2__schedule(fn) { + Services.tm.mainThread.dispatch(fn.bind(this), + Ci.nsIThread.DISPATCH_NORMAL); + }, + + addObserverForName: function CPS2_addObserverForName(name, observer) { + this._cps.addObserver(name, observer); + }, + + removeObserverForName: function CPS2_removeObserverForName(name, observer) { + this._cps.removeObserver(name, observer); + }, + + /** + * Tests use this as a backchannel by calling it directly. + * + * @param subj This value depends on topic. + * @param topic The backchannel "method" name. + * @param data This value depends on topic. + */ + observe: function CPS2_observe(subj, topic, data) { + switch (topic) { + case "test:reset": + let fn = subj.QueryInterface(Ci.xpcIJSWeakReference).get(); + this._reset(fn); + break; + case "test:db": + let obj = subj.QueryInterface(Ci.xpcIJSWeakReference).get(); + obj.value = this._cps._dbConnection; + break; + } + }, + + /** + * Removes all state from the service. Used by tests. + * + * @param callback A function that will be called when done. + */ + _reset: function CPS2__reset(callback) { + this._pbStore.removeAll(); + this._cache.removeAll(); + + let cps = this._cps; + cps._observers = {}; + cps._genericObservers = []; + + let tables = ["prefs", "groups", "settings"]; + let stmts = tables.map(function (t) this._stmt("DELETE FROM", t), this); + this._execStmts(stmts, { onDone: function () callback() }); + }, + + QueryInterface: function CPS2_QueryInterface(iid) { + let supportedIIDs = [ + Ci.nsIContentPrefService, + Ci.nsIObserver, + Ci.nsISupports, + ]; + if (supportedIIDs.some(function (i) iid.equals(i))) + return this; + if (iid.equals(Ci.nsIContentPrefService)) + return this._cps; + throw Cr.NS_ERROR_NO_INTERFACE; + }, +}; + +function ContentPref(domain, name, value) { + this.domain = domain; + this.name = name; + this.value = value; +} + +ContentPref.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPref]), +}; + +function cbHandleResult(callback, pref) { + safeCallback(callback, "handleResult", [pref]); +} + +function cbHandleCompletion(callback, reason) { + safeCallback(callback, "handleCompletion", [reason]); +} + +function cbHandleError(callback, nsresult) { + safeCallback(callback, "handleError", [nsresult]); +} + +function safeCallback(callbackObj, methodName, args) { + if (!callbackObj || typeof(callbackObj[methodName]) != "function") + return; + try { + callbackObj[methodName].apply(callbackObj, args); + } + catch (err) { + Cu.reportError(err); + } +} + +function checkGroupArg(group) { + if (!group || typeof(group) != "string") + throw invalidArg("domain must be nonempty string."); +} + +function checkNameArg(name) { + if (!name || typeof(name) != "string") + throw invalidArg("name must be nonempty string."); +} + +function checkValueArg(value) { + if (value === undefined) + throw invalidArg("value must not be undefined."); +} + +function checkCallbackArg(callback, required) { + if (callback && !(callback instanceof Ci.nsIContentPrefCallback2)) + throw invalidArg("callback must be an nsIContentPrefCallback2."); + if (!callback && required) + throw invalidArg("callback must be given."); +} + +function invalidArg(msg) { + return Components.Exception(msg, Cr.NS_ERROR_INVALID_ARG); +} + +function joinArgs(args) { + return Array.join(args, " ").trim().replace(/\s{2,}/g, " "); +} diff --git a/toolkit/components/contentprefs/ContentPrefStore.jsm b/toolkit/components/contentprefs/ContentPrefStore.jsm new file mode 100644 index 000000000000..1ff73fdb63f7 --- /dev/null +++ b/toolkit/components/contentprefs/ContentPrefStore.jsm @@ -0,0 +1,115 @@ +/* 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/. */ + +let EXPORTED_SYMBOLS = [ + "ContentPrefStore", +]; + +function ContentPrefStore() { + this._groups = {}; + this._globalNames = {}; +} + +ContentPrefStore.prototype = { + + set: function CPS_set(group, name, val) { + if (group) { + if (!this._groups.hasOwnProperty(group)) + this._groups[group] = {}; + this._groups[group][name] = val; + } + else { + this._globalNames[name] = val; + } + }, + + setWithCast: function CPS_setWithCast(group, name, val) { + if (typeof(val) == "boolean") + val = val ? 1 : 0; + else if (val === undefined) + val = null; + this.set(group, name, val); + }, + + has: function CPS_has(group, name) { + return (group && + this._groups.hasOwnProperty(group) && + this._groups[group].hasOwnProperty(name)) || + (!group && + this._globalNames.hasOwnProperty(name)); + }, + + get: function CPS_get(group, name) { + if (group) { + if (this._groups.hasOwnProperty(group) && + this._groups[group].hasOwnProperty(name)) + return this._groups[group][name]; + } + else if (this._globalNames.hasOwnProperty(name)) { + return this._globalNames[name]; + } + return undefined; + }, + + remove: function CPS_remove(group, name) { + if (group) { + if (this._groups.hasOwnProperty(group)) { + delete this._groups[group][name]; + if (!Object.keys(this._groups[group]).length) + delete this._groups[group]; + } + } + else { + delete this._globalNames[name]; + } + }, + + removeGrouped: function CPS_removeGrouped() { + this._groups = {}; + }, + + removeAll: function CPS_removeAll() { + this.removeGrouped(); + this._globalNames = {}; + }, + + __iterator__: function CPS___iterator__() { + for (let [group, names] in Iterator(this._groups)) { + for (let [name, val] in Iterator(names)) { + yield [group, name, val]; + } + } + for (let [name, val] in Iterator(this._globalNames)) { + yield [null, name, val]; + } + }, + + match: function CPS_match(group, name, includeSubdomains) { + for (let sgroup in this.matchGroups(group, includeSubdomains)) { + if (this.has(sgroup, name)) + yield [sgroup, this.get(sgroup, name)]; + } + }, + + matchGroups: function CPS_matchGroups(group, includeSubdomains) { + if (group) { + if (includeSubdomains) { + for (let [sgroup, , ] in this) { + if (sgroup) { + let idx = sgroup.indexOf(group); + if (idx == sgroup.length - group.length && + (idx == 0 || sgroup[idx - 1] == ".")) + yield sgroup; + } + } + } + else if (this._groups.hasOwnProperty(group)) { + yield group; + } + } + else if (Object.keys(this._globalNames).length) { + yield null; + } + }, +}; diff --git a/toolkit/components/contentprefs/Makefile.in b/toolkit/components/contentprefs/Makefile.in index d67fbeaf6fc5..5203817853f4 100644 --- a/toolkit/components/contentprefs/Makefile.in +++ b/toolkit/components/contentprefs/Makefile.in @@ -11,9 +11,16 @@ include $(DEPTH)/config/autoconf.mk MODULE = contentprefs -EXTRA_COMPONENTS = nsContentPrefService.js nsContentPrefService.manifest +EXTRA_COMPONENTS = \ + nsContentPrefService.js \ + nsContentPrefService.manifest \ + $(NULL) -EXTRA_JS_MODULES = ContentPrefInstance.jsm +EXTRA_JS_MODULES = \ + ContentPrefInstance.jsm \ + ContentPrefService2.jsm \ + ContentPrefStore.jsm \ + $(NULL) TEST_DIRS += tests diff --git a/toolkit/components/contentprefs/nsContentPrefService.js b/toolkit/components/contentprefs/nsContentPrefService.js index e50765ed3736..06dafc6c5a09 100644 --- a/toolkit/components/contentprefs/nsContentPrefService.js +++ b/toolkit/components/contentprefs/nsContentPrefService.js @@ -106,61 +106,48 @@ function ContentPrefService() { this._observerSvc.addObserver(this, "xpcom-shutdown", false); } -var inMemoryPrefsProto = { - getPref: function(aName, aGroup) { - aGroup = aGroup || "__GlobalPrefs__"; - - if (this._prefCache[aGroup] && this._prefCache[aGroup].has(aName)) { - let value = this._prefCache[aGroup].get(aName); - return [true, value]; - } - return [false, undefined]; - }, - - setPref: function(aName, aValue, aGroup) { - if (typeof aValue == "boolean") - aValue = aValue ? 1 : 0; - else if (aValue === undefined) - aValue = null; - - this.cachePref(aName, aValue, aGroup); - }, - - removePref: function(aName, aGroup) { - aGroup = aGroup || "__GlobalPrefs__"; - - if (this._prefCache[aGroup].has(aName)) { - this._prefCache[aGroup].delete(aName); - if (this._prefCache[aGroup].size == 0) { - // remove empty group - delete this._prefCache[aGroup]; - } - } - }, - - invalidate: function(aKeepGlobal) { - if (!aKeepGlobal) { - this._prefCache = {}; - return; - } - - if (this._prefCache.hasOwnProperty("__GlobalPrefs__")) { - let globals = this._prefCache["__GlobalPrefs__"]; - this._prefCache = {"__GlobalPrefs__": globals}; - } else { - this._prefCache = {}; +Cu.import("resource://gre/modules/ContentPrefStore.jsm"); +const cache = new ContentPrefStore(); +cache.set = function CPS_cache_set(group, name, val) { + Object.getPrototypeOf(this).set.apply(this, arguments); + let groupCount = Object.keys(this._groups).length; + if (groupCount >= CACHE_MAX_GROUP_ENTRIES) { + // Clean half of the entries + for (let [group, name, ] in this) { + this.remove(group, name); + groupCount--; + if (groupCount < CACHE_MAX_GROUP_ENTRIES / 2) + break; } } }; +const privModeStorage = new ContentPrefStore(); + ContentPrefService.prototype = { //**************************************************************************// // XPCOM Plumbing - classID: Components.ID("{e3f772f3-023f-4b32-b074-36cf0fd5d414}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPrefService, - Ci.nsIMessageListener]), + classID: Components.ID("{e3f772f3-023f-4b32-b074-36cf0fd5d414}"), + QueryInterface: function CPS_QueryInterface(iid) { + let supportedIIDs = [ + Ci.nsIContentPrefService, + Ci.nsIFrameMessageListener, + Ci.nsISupports, + ]; + if (supportedIIDs.some(function (i) iid.equals(i))) + return this; + if (iid.equals(Ci.nsIContentPrefService2)) { + if (!this._contentPrefService2) { + let s = {}; + Cu.import("resource://gre/modules/ContentPrefService2.jsm", s); + this._contentPrefService2 = new s.ContentPrefService2(this); + } + return this._contentPrefService2; + } + throw Cr.NS_ERROR_NO_INTERFACE; + }, //**************************************************************************// // Convenience Getters @@ -263,6 +250,9 @@ ContentPrefService.prototype = { this.__stmtUpdatePref = null; } + if (this._contentPrefService2) + this._contentPrefService2.destroy(); + this._dbConnection.asyncClose(); // Delete references to XPCOM components to make sure we don't leak them @@ -286,94 +276,17 @@ ContentPrefService.prototype = { this._destroy(); break; case "last-pb-context-exited": - this._privModeStorage.invalidate(); + this._privModeStorage.removeAll(); break; } }, //**************************************************************************// - // Prefs cache - _cache: Object.create(inMemoryPrefsProto, { - _prefCache: { - value: {}, configurable: true, writable: true, enumerable: true - }, + // in-memory cache and private-browsing stores - cachePref: { value: - function(aName, aValue, aGroup) { - aGroup = aGroup || "__GlobalPrefs__"; - - if (!this._prefCache[aGroup]) { - this._possiblyCleanCache(); - this._prefCache[aGroup] = new Map(); - } - - this._prefCache[aGroup].set(aName, aValue); - } - }, - - _possiblyCleanCache: { value: - function() { - let groupCount = Object.keys(this._prefCache).length; - - if (groupCount >= CACHE_MAX_GROUP_ENTRIES) { - // Clean half of the entries - for (let entry in this._prefCache) { - delete this._prefCache[entry]; - groupCount--; - - if (groupCount < CACHE_MAX_GROUP_ENTRIES / 2) - break; - } - } - } - } - }), - - //**************************************************************************// - // Private mode storage - _privModeStorage: Object.create(inMemoryPrefsProto, { - _prefCache: { - value: {}, configurable: true, writable: true, enumerable: true - }, - - cachePref: { value: - function(aName, aValue, aGroup) { - aGroup = aGroup || "__GlobalPrefs__"; - - if (!this._prefCache[aGroup]) { - this._prefCache[aGroup] = new Map(); - } - - this._prefCache[aGroup].set(aName, aValue); - } - }, - - getPrefs: { value: - function(aGroup) { - aGroup = aGroup || "__GlobalPrefs__"; - if (this._prefCache[aGroup]) { - return [true, this._prefCache[aGroup]]; - } - return [false, undefined]; - } - }, - - groupsForName: { value: - function(aName) { - var res = []; - for (let entry in this._prefCache) { - if (this._prefCache[entry]) { - if (entry === "__GlobalPrefs__") { - entry = null; - } - res.push(entry); - } - } - return res; - } - } - }), + _cache: cache, + _privModeStorage: privModeStorage, //**************************************************************************// // nsIContentPrefService @@ -386,8 +299,8 @@ ContentPrefService.prototype = { var group = this._parseGroupParam(aGroup); if (aContext && aContext.usePrivateBrowsing) { - let [haspref, value] = this._privModeStorage.getPref(aName, group); - if (haspref) { + if (this._privModeStorage.has(group, aName)) { + let value = this._privModeStorage.get(group, aName); if (aCallback) { this._scheduleCallback(function(){aCallback.onResult(value);}); return; @@ -414,7 +327,7 @@ ContentPrefService.prototype = { var group = this._parseGroupParam(aGroup); if (aContext && aContext.usePrivateBrowsing) { - this._privModeStorage.setPref(aName, aValue, group); + this._privModeStorage.setWithCast(group, aName, aValue); this._notifyPrefSet(group, aName, aValue); return; } @@ -436,7 +349,7 @@ ContentPrefService.prototype = { else this._insertPref(groupID, settingID, aValue); - this._cache.setPref(aName, aValue, group); + this._cache.setWithCast(group, aName, aValue); this._notifyPrefSet(group, aName, aValue); }, @@ -454,8 +367,7 @@ ContentPrefService.prototype = { let group = this._parseGroupParam(aGroup); let storage = aContext && aContext.usePrivateBrowsing ? this._privModeStorage: this._cache; - let [cached,] = storage.getPref(aName, group); - return cached; + return storage.has(group, aName); }, removePref: function ContentPrefService_removePref(aGroup, aName, aContext) { @@ -466,7 +378,7 @@ ContentPrefService.prototype = { var group = this._parseGroupParam(aGroup); if (aContext && aContext.usePrivateBrowsing) { - this._privModeStorage.removePref(aName, group); + this._privModeStorage.remove(group, aName); this._notifyPrefRemoved(group, aName); return; } @@ -489,7 +401,7 @@ ContentPrefService.prototype = { if (groupID) this._deleteGroupIfUnused(groupID); - this._cache.removePref(aName, group); + this._cache.remove(group, aName); this._notifyPrefRemoved(group, aName); }, @@ -497,9 +409,9 @@ ContentPrefService.prototype = { // will not delete global preferences if (aContext && aContext.usePrivateBrowsing) { // keep only global prefs - this._privModeStorage.invalidate(true); + this._privModeStorage.removeGrouped(); } - this._cache.invalidate(true); + this._cache.removeGrouped(); this._dbConnection.beginTransaction(); try { this._dbConnection.executeSimpleSQL("DELETE FROM prefs WHERE groupID IS NOT NULL"); @@ -522,11 +434,11 @@ ContentPrefService.prototype = { Cr.NS_ERROR_ILLEGAL_VALUE); if (aContext && aContext.usePrivateBrowsing) { - let groupNames = this._privModeStorage.groupsForName(aName); - for (var i = 0; i < groupNames.length; i++) { - let groupName = groupNames[i]; - this._privModeStorage.removePref(aName, groupName); - this._notifyPrefRemoved(groupName, aName); + for (let [group, name, ] in this._privModeStorage) { + if (name === aName) { + this._privModeStorage.remove(group, aName); + this._notifyPrefRemoved(group, aName); + } } } @@ -563,7 +475,7 @@ ContentPrefService.prototype = { this._dbConnection.executeSimpleSQL("DELETE FROM settings WHERE id = " + settingID); for (var i = 0; i < groupNames.length; i++) { - this._cache.removePref(aName, groupNames[i]); + this._cache.remove(groupNames[i], aName); if (groupNames[i]) // ie. not null, which will be last (and i == groupIDs.length) this._deleteGroupIfUnused(groupIDs[i]); if (!aContext || !aContext.usePrivateBrowsing) { @@ -577,9 +489,9 @@ ContentPrefService.prototype = { if (aContext && aContext.usePrivateBrowsing) { let prefs = Cc["@mozilla.org/hash-property-bag;1"]. createInstance(Ci.nsIWritablePropertyBag); - let [hasbranch,properties] = this._privModeStorage.getPrefs(group); - for (let [entry, value] of properties) { - prefs.setProperty(entry, value); + for (let [sgroup, sname, sval] in this._privModeStorage) { + if (sgroup === group) + prefs.setProperty(sname, sval); } return prefs; } @@ -597,11 +509,9 @@ ContentPrefService.prototype = { if (aContext && aContext.usePrivateBrowsing) { let prefs = Cc["@mozilla.org/hash-property-bag;1"]. createInstance(Ci.nsIWritablePropertyBag); - let groupNames = this._privModeStorage.groupsForName(aName); - for (var i = 0; i < groupNames.length; i++) { - let groupName = groupNames[i]; - prefs.setProperty(groupName, - this._privModeStorage.getPref(aName, groupName)[1]); + for (let [sgroup, sname, sval] in this._privModeStorage) { + if (sname === aName) + prefs.setProperty(sgroup, sval); } return prefs; } @@ -719,8 +629,9 @@ ContentPrefService.prototype = { }, _selectPref: function ContentPrefService__selectPref(aGroup, aSetting, aCallback) { - let [cached, value] = this._cache.getPref(aSetting, aGroup); - if (cached) { + let value = undefined; + if (this._cache.has(aGroup, aSetting)) { + value = this._cache.get(aGroup, aSetting); if (aCallback) { this._scheduleCallback(function(){aCallback.onResult(value);}); return; @@ -735,7 +646,7 @@ ContentPrefService.prototype = { if (aCallback) { let cache = this._cache; new AsyncStatement(this._stmtSelectPref).execute({onResult: function(aResult) { - cache.cachePref(aSetting, aResult, aGroup); + cache.set(aGroup, aSetting, aResult); aCallback.onResult(aResult); }}); } @@ -743,7 +654,7 @@ ContentPrefService.prototype = { if (this._stmtSelectPref.executeStep()) { value = this._stmtSelectPref.row["value"]; } - this._cache.cachePref(aSetting, value, aGroup); + this._cache.set(aGroup, aSetting, value); } } finally { @@ -768,8 +679,9 @@ ContentPrefService.prototype = { }, _selectGlobalPref: function ContentPrefService__selectGlobalPref(aName, aCallback) { - let [cached, value] = this._cache.getPref(aName, null); - if (cached) { + let value = undefined; + if (this._cache.has(null, aName)) { + value = this._cache.get(null, aName); if (aCallback) { this._scheduleCallback(function(){aCallback.onResult(value);}); return; @@ -783,7 +695,7 @@ ContentPrefService.prototype = { if (aCallback) { let cache = this._cache; new AsyncStatement(this._stmtSelectGlobalPref).execute({onResult: function(aResult) { - cache.cachePref(aName, aResult); + cache.set(null, aName, aResult); aCallback.onResult(aResult); }}); } @@ -791,7 +703,7 @@ ContentPrefService.prototype = { if (this._stmtSelectGlobalPref.executeStep()) { value = this._stmtSelectGlobalPref.row["value"]; } - this._cache.cachePref(aName, value); + this._cache.set(null, aName, value); } } finally { diff --git a/toolkit/components/contentprefs/tests/Makefile.in b/toolkit/components/contentprefs/tests/Makefile.in index 35e8ef0e02db..ca889ee488a0 100644 --- a/toolkit/components/contentprefs/tests/Makefile.in +++ b/toolkit/components/contentprefs/tests/Makefile.in @@ -13,14 +13,11 @@ include $(DEPTH)/config/autoconf.mk MODULE = test_toolkit_contentprefs -ifdef MOZ_PHOENIX -XPCSHELL_TESTS = unit +XPCSHELL_TESTS = unit unit_cps2 # FIXME/bug 575918: out-of-process xpcshell is broken on OS X ifneq ($(OS_ARCH),Darwin) XPCSHELL_TESTS += unit_ipc endif -endif - include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.jsm b/toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.jsm new file mode 100644 index 000000000000..b79b23c95cb7 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/AsyncRunner.jsm @@ -0,0 +1,69 @@ +/* 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/. */ + +let EXPORTED_SYMBOLS = [ + "AsyncRunner", +]; + +const { interfaces: Ci, classes: Cc } = Components; + +function AsyncRunner(callbacks) { + this._callbacks = callbacks; + this._iteratorQueue = []; + + // This catches errors reported to the console, e.g., via Cu.reportError. + Cc["@mozilla.org/consoleservice;1"]. + getService(Ci.nsIConsoleService). + registerListener(this); +} + +AsyncRunner.prototype = { + + appendIterator: function AR_appendIterator(iter) { + this._iteratorQueue.push(iter); + }, + + next: function AR_next(/* ... */) { + if (!this._iteratorQueue.length) { + this.destroy(); + this._callbacks.done(); + return; + } + + // send() discards all arguments after the first, so there's no choice here + // but to send only one argument to the yielder. + let args = [arguments.length <= 1 ? arguments[0] : Array.slice(arguments)]; + try { + var val = this._iteratorQueue[0].send.apply(this._iteratorQueue[0], args); + } + catch (err if err instanceof StopIteration) { + this._iteratorQueue.shift(); + this.next(); + return; + } + catch (err) { + this._callbacks.error(err); + } + + // val is truthy => call next + // val is an iterator => prepend it to the queue and start on it + if (val) { + if (typeof(val) != "boolean") + this._iteratorQueue.unshift(val); + this.next(); + } + }, + + destroy: function AR_destroy() { + Cc["@mozilla.org/consoleservice;1"]. + getService(Ci.nsIConsoleService). + unregisterListener(this); + this.destroy = function AR_alreadyDestroyed() {}; + }, + + observe: function AR_consoleServiceListener(msg) { + if (msg instanceof Ci.nsIScriptError) + this._callbacks.consoleError(msg); + }, +}; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/head.js b/toolkit/components/contentprefs/tests/unit_cps2/head.js new file mode 100644 index 000000000000..ac0c45a2c86b --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/head.js @@ -0,0 +1,311 @@ +/* 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/. */ + +const { interfaces: Ci, classes: Cc, results: Cr, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Services.jsm"); + +var cps; +var asyncRunner; +var next; + +(function init() { + // There has to be a profile directory before the CPS service is gotten. + do_get_profile(); +})(); + +function runAsyncTests(tests) { + do_test_pending(); + + cps = Cc["@mozilla.org/content-pref/service;1"]. + getService(Ci.nsIContentPrefService2); + + // Without this the private-browsing service tries to open a dialog when you + // change its enabled state. + Services.prefs.setBoolPref("browser.privatebrowsing.keep_current_session", + true); + + let s = {}; + Cu.import("resource://test/AsyncRunner.jsm", s); + asyncRunner = new s.AsyncRunner({ + done: do_test_finished, + error: function (err) { + // xpcshell test functions like do_check_eq throw NS_ERROR_ABORT on + // failure. Ignore those and catch only uncaught exceptions. + if (err !== Cr.NS_ERROR_ABORT) { + if (err.stack) { + err = err + "\n\nTraceback (most recent call first):\n" + err.stack + + "\nUseless do_throw stack:"; + } + do_throw(err); + } + }, + consoleError: function (scriptErr) { + // As much as possible make sure the error is related to the test. On the + // other hand if this fails to catch a test-related error, we'll hang. + let filename = scriptErr.sourceName || scriptErr.toString() || ""; + if (/contentpref/i.test(filename)) + do_throw(scriptErr); + } + }); + + next = asyncRunner.next.bind(asyncRunner); + + do_register_cleanup(function () { + asyncRunner.destroy(); + asyncRunner = null; + }); + + tests.forEach(function (test) { + function gen() { + do_print("Running " + test.name); + yield test(); + yield reset(); + } + asyncRunner.appendIterator(gen()); + }); + + // reset() ends up calling asyncRunner.next(), starting the tests. + reset(); +} + +function makeCallback(callbacks) { + callbacks = callbacks || {}; + ["handleResult", "handleError"].forEach(function (meth) { + if (!callbacks[meth]) + callbacks[meth] = function () { + do_throw(meth + " shouldn't be called."); + }; + }); + if (!callbacks.handleCompletion) + callbacks.handleCompletion = function (reason) { + do_check_eq(reason, Ci.nsIContentPrefCallback2.COMPLETE_OK); + next(); + }; + return callbacks; +} + +function do_check_throws(fn) { + let threw = false; + try { + fn(); + } + catch (err) { + threw = true; + } + do_check_true(threw); +} + +function sendMessage(msg, callback) { + let obj = callback || {}; + let ref = Cu.getWeakReference(obj); + cps.QueryInterface(Ci.nsIObserver).observe(ref, "test:" + msg, null); + return "value" in obj ? obj.value : undefined; +} + +function reset() { + sendMessage("reset", next); +} + +function set(group, name, val, context) { + cps.set(group, name, val, context, makeCallback()); +} + +function setGlobal(name, val, context) { + cps.setGlobal(name, val, context, makeCallback()); +} + +function prefOK(actual, expected, strict) { + do_check_true(actual instanceof Ci.nsIContentPref); + do_check_eq(actual.domain, expected.domain); + do_check_eq(actual.name, expected.name); + if (strict) + do_check_true(actual.value === expected.value); + else + do_check_eq(actual.value, expected.value); +} + +function getOK(args, expectedVal, expectedGroup, strict) { + if (args.length == 2) + args.push(undefined); + let expectedPrefs = expectedVal === undefined ? [] : + [{ domain: expectedGroup || args[0], + name: args[1], + value: expectedVal }]; + yield getOKEx("getByDomainAndName", args, expectedPrefs, strict); +} + +function getSubdomainsOK(args, expectedGroupValPairs) { + if (args.length == 2) + args.push(undefined); + let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) { + return { domain: group, name: args[1], value: val }; + }); + yield getOKEx("getBySubdomainAndName", args, expectedPrefs); +} + +function getGlobalOK(args, expectedVal) { + if (args.length == 1) + args.push(undefined); + let expectedPrefs = expectedVal === undefined ? [] : + [{ domain: null, name: args[0], value: expectedVal }]; + yield getOKEx("getGlobal", args, expectedPrefs); +} + +function getOKEx(methodName, args, expectedPrefs, strict, context) { + let actualPrefs = []; + args.push(makeCallback({ + handleResult: function (pref) actualPrefs.push(pref) + })); + yield cps[methodName].apply(cps, args); + arraysOfArraysOK([actualPrefs], [expectedPrefs], function (actual, expected) { + prefOK(actual, expected, strict); + }); +} + +function getCachedOK(args, expectedIsCached, expectedVal, expectedGroup, + strict) { + if (args.length == 2) + args.push(undefined); + let expectedPref = !expectedIsCached ? null : { + domain: expectedGroup || args[0], + name: args[1], + value: expectedVal + }; + getCachedOKEx("getCachedByDomainAndName", args, expectedPref, strict); +} + +function getCachedSubdomainsOK(args, expectedGroupValPairs) { + if (args.length == 2) + args.push(undefined); + let len = {}; + args.push(len); + let actualPrefs = cps.getCachedBySubdomainAndName.apply(cps, args); + do_check_eq(actualPrefs.length, len.value); + let expectedPrefs = expectedGroupValPairs.map(function ([group, val]) { + return { domain: group, name: args[1], value: val }; + }); + arraysOfArraysOK([actualPrefs], [expectedPrefs], prefOK); +} + +function getCachedGlobalOK(args, expectedIsCached, expectedVal) { + if (args.length == 1) + args.push(undefined); + let expectedPref = !expectedIsCached ? null : { + domain: null, + name: args[0], + value: expectedVal + }; + getCachedOKEx("getCachedGlobal", args, expectedPref); +} + +function getCachedOKEx(methodName, args, expectedPref, strict) { + let actualPref = cps[methodName].apply(cps, args); + if (expectedPref) + prefOK(actualPref, expectedPref, strict); + else + do_check_true(actualPref === null); +} + +function arraysOfArraysOK(actual, expected, cmp) { + cmp = cmp || function (a, b) do_check_eq(a, b); + do_check_eq(actual.length, expected.length); + actual.forEach(function (actualChildArr, i) { + let expectedChildArr = expected[i]; + do_check_eq(actualChildArr.length, expectedChildArr.length); + actualChildArr.forEach(function (actualElt, j) { + let expectedElt = expectedChildArr[j]; + cmp(actualElt, expectedElt); + }); + }); +} + +function dbOK(expectedRows) { + let db = sendMessage("db"); + let stmt = db.createAsyncStatement( + "SELECT groups.name AS grp, settings.name AS name, prefs.value AS value " + + "FROM prefs " + + "LEFT JOIN groups ON groups.id = prefs.groupID " + + "LEFT JOIN settings ON settings.id = prefs.settingID " + + "UNION " + + + // These second two SELECTs get the rows of the groups and settings tables + // that aren't referenced by the prefs table. Neither should return any + // rows if the component is working properly. + "SELECT groups.name AS grp, NULL AS name, NULL AS value " + + "FROM groups " + + "WHERE id NOT IN (" + + "SELECT DISTINCT groupID " + + "FROM prefs " + + "WHERE groupID NOTNULL" + + ") " + + "UNION " + + "SELECT NULL AS grp, settings.name AS name, NULL AS value " + + "FROM settings " + + "WHERE id NOT IN (" + + "SELECT DISTINCT settingID " + + "FROM prefs " + + "WHERE settingID NOTNULL" + + ") " + + + "ORDER BY value ASC, grp ASC, name ASC" + ); + + let actualRows = []; + let cols = ["grp", "name", "value"]; + + db.executeAsync([stmt], 1, { + handleCompletion: function (reason) { + arraysOfArraysOK(actualRows, expectedRows); + next(); + }, + handleResult: function (results) { + let row = null; + while (row = results.getNextRow()) { + actualRows.push(cols.map(function (c) row.getResultByName(c))); + } + }, + handleError: function (err) { + do_throw(err); + } + }); + stmt.finalize(); +} + +function on(event, names) { + let args = { + reset: function () { + for (let prop in this) { + if (Array.isArray(this[prop])) + this[prop].splice(0, this[prop].length); + } + }, + destroy: function () { + names.forEach(function (n) cps.removeObserverForName(n, observers[n])); + }, + }; + + let observers = {}; + + names.forEach(function (name) { + let obs = {}; + ["onContentPrefSet", "onContentPrefRemoved"].forEach(function (meth) { + obs[meth] = function () do_throw(meth + " should not be called"); + }); + obs["onContentPref" + event] = function () { + args[name].push(Array.slice(arguments)); + }; + observers[name] = obs; + args[name] = []; + args[name].observer = obs; + cps.addObserverForName(name, obs); + }); + + return args; +} + +function observerArgsOK(actualArgs, expectedArgs) { + do_check_neq(actualArgs, undefined); + arraysOfArraysOK(actualArgs, expectedArgs); +} diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js b/toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js new file mode 100644 index 000000000000..07f2188a9115 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_getCached.js @@ -0,0 +1,99 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function nonexistent() { + getCachedOK(["a.com", "foo"], false, undefined); + getCachedGlobalOK(["foo"], false, undefined); + yield true; + }, + + function isomorphicDomains() { + yield set("a.com", "foo", 1); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["http://a.com/huh", "foo"], true, 1, "a.com"); + }, + + function names() { + yield set("a.com", "foo", 1); + getCachedOK(["a.com", "foo"], true, 1); + + yield set("a.com", "bar", 2); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + + yield setGlobal("foo", 3); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + getCachedGlobalOK(["foo"], true, 3); + + yield setGlobal("bar", 4); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + }, + + function subdomains() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "foo", 2); + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["b.a.com", "foo"], true, 2); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 6, context); + yield setGlobal("foo", 7, context); + getCachedOK(["a.com", "foo", context], true, 6); + getCachedOK(["a.com", "bar", context], true, 2); + getCachedGlobalOK(["foo", context], true, 7); + getCachedGlobalOK(["bar", context], true, 4); + getCachedOK(["b.com", "foo", context], true, 5); + + getCachedOK(["a.com", "foo"], true, 1); + getCachedOK(["a.com", "bar"], true, 2); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + getCachedOK(["b.com", "foo"], true, 5); + }, + + function erroneous() { + do_check_throws(function () + cps.getCachedByDomainAndName(null, "foo", null)); + do_check_throws(function () + cps.getCachedByDomainAndName("", "foo", null)); + do_check_throws(function () + cps.getCachedByDomainAndName("a.com", "", null)); + do_check_throws(function () + cps.getCachedByDomainAndName("a.com", null, null)); + do_check_throws(function () cps.getCachedGlobal("", null)); + do_check_throws(function () cps.getCachedGlobal(null, null)); + yield true; + }, + + function casts() { + // SQLite casts booleans to integers. This makes sure the values stored in + // the cache are the same as the casted values in the database. + + yield set("a.com", "foo", false); + yield getOK(["a.com", "foo"], 0, "a.com", true); + getCachedOK(["a.com", "foo"], true, 0, "a.com", true); + + yield set("a.com", "bar", true); + yield getOK(["a.com", "bar"], 1, "a.com", true); + getCachedOK(["a.com", "bar"], true, 1, "a.com", true); + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js b/toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js new file mode 100644 index 000000000000..239211000a93 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_getCachedSubdomains.js @@ -0,0 +1,190 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function nonexistent() { + getCachedSubdomainsOK(["a.com", "foo"], []); + yield true; + }, + + function isomorphicDomains() { + yield set("a.com", "foo", 1); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["http://a.com/huh", "foo"], [["a.com", 1]]); + }, + + function names() { + yield set("a.com", "foo", 1); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + + yield set("a.com", "bar", 2); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + + yield setGlobal("foo", 3); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, 3); + + yield setGlobal("bar", 4); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + }, + + function subdomains() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "foo", 2); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1], ["b.a.com", 2]]); + getCachedSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + }, + + function populateViaGet() { + yield cps.getByDomainAndName("a.com", "foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + + yield cps.getGlobal("foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + }, + + function populateViaGetSubdomains() { + yield cps.getBySubdomainAndName("a.com", "foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + }, + + function populateViaRemove() { + yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + + yield cps.removeBySubdomainAndName("b.com", "foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + + yield cps.removeGlobal("foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + + yield set("a.com", "foo", 1); + yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + + yield set("a.com", "foo", 2); + yield set("b.a.com", "foo", 3); + yield cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], + [["a.com", undefined], ["b.a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedSubdomainsOK(["b.a.com", "foo"], [["b.a.com", undefined]]); + + yield setGlobal("foo", 4); + yield cps.removeGlobal("foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], + [["a.com", undefined], ["b.a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedSubdomainsOK(["b.a.com", "foo"], [["b.a.com", undefined]]); + }, + + function populateViaRemoveByDomain() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield set("b.a.com", "foo", 3); + yield set("b.a.com", "bar", 4); + yield cps.removeByDomain("a.com", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], + [["a.com", undefined], ["b.a.com", 3]]); + getCachedSubdomainsOK(["a.com", "bar"], + [["a.com", undefined], ["b.a.com", 4]]); + + yield set("a.com", "foo", 5); + yield set("a.com", "bar", 6); + yield cps.removeBySubdomain("a.com", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], + [["a.com", undefined], ["b.a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], + [["a.com", undefined], ["b.a.com", undefined]]); + + yield setGlobal("foo", 7); + yield setGlobal("bar", 8); + yield cps.removeAllGlobals(null, makeCallback()); + getCachedGlobalOK(["foo"], true, undefined); + getCachedGlobalOK(["bar"], true, undefined); + }, + + function populateViaRemoveAllDomains() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield set("b.com", "foo", 3); + yield set("b.com", "bar", 4); + yield cps.removeAllDomains(null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", undefined]]); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", undefined]]); + getCachedSubdomainsOK(["b.com", "bar"], [["b.com", undefined]]); + }, + + function populateViaRemoveByName() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield cps.removeByName("foo", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedGlobalOK(["bar"], true, 4); + + yield cps.removeByName("bar", null, makeCallback()); + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", undefined]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", undefined]]); + getCachedGlobalOK(["foo"], true, undefined); + getCachedGlobalOK(["bar"], true, undefined); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 6, context); + yield setGlobal("foo", 7, context); + getCachedSubdomainsOK(["a.com", "foo", context], [["a.com", 6]]); + getCachedSubdomainsOK(["a.com", "bar", context], [["a.com", 2]]); + getCachedGlobalOK(["foo", context], true, 7); + getCachedGlobalOK(["bar", context], true, 4); + getCachedSubdomainsOK(["b.com", "foo", context], [["b.com", 5]]); + + getCachedSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + getCachedSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + getCachedGlobalOK(["foo"], true, 3); + getCachedGlobalOK(["bar"], true, 4); + getCachedSubdomainsOK(["b.com", "foo"], [["b.com", 5]]); + }, + + function erroneous() { + do_check_throws(function () + cps.getCachedBySubdomainAndName(null, "foo", null)); + do_check_throws(function () + cps.getCachedBySubdomainAndName("", "foo", null)); + do_check_throws(function () + cps.getCachedBySubdomainAndName("a.com", "", null)); + do_check_throws(function () + cps.getCachedBySubdomainAndName("a.com", null, null)); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js b/toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js new file mode 100644 index 000000000000..3d32584ca473 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_getSubdomains.js @@ -0,0 +1,73 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function get_nonexistent() { + yield getSubdomainsOK(["a.com", "foo"], []); + }, + + function isomorphicDomains() { + yield set("a.com", "foo", 1); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + yield getSubdomainsOK(["http://a.com/huh", "foo"], [["a.com", 1]]); + }, + + function names() { + yield set("a.com", "foo", 1); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + + yield set("a.com", "bar", 2); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + yield getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + + yield setGlobal("foo", 3); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + yield getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + }, + + function subdomains() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "foo", 2); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1], ["b.a.com", 2]]); + yield getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 6, context); + yield setGlobal("foo", 7, context); + yield getSubdomainsOK(["a.com", "foo", context], [["a.com", 6]]); + yield getSubdomainsOK(["a.com", "bar", context], [["a.com", 2]]); + yield getSubdomainsOK(["b.com", "foo", context], [["b.com", 5]]); + + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 1]]); + yield getSubdomainsOK(["a.com", "bar"], [["a.com", 2]]); + yield getSubdomainsOK(["b.com", "foo"], [["b.com", 5]]); + }, + + function erroneous() { + do_check_throws(function () + cps.getBySubdomainAndName(null, "foo", null, {})); + do_check_throws(function () + cps.getBySubdomainAndName("", "foo", null, {})); + do_check_throws(function () + cps.getBySubdomainAndName("a.com", "", null, {})); + do_check_throws(function () + cps.getBySubdomainAndName("a.com", null, null, {})); + do_check_throws(function () + cps.getBySubdomainAndName("a.com", "foo", null, null)); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js new file mode 100644 index 000000000000..421d0693068f --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_observers.js @@ -0,0 +1,125 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function observerForName_set() { + let args = on("Set", ["foo", null, "bar"]); + + yield set("a.com", "foo", 1); + observerArgsOK(args.foo, [["a.com", "foo", 1]]); + observerArgsOK(args.null, [["a.com", "foo", 1]]); + observerArgsOK(args.bar, []); + args.reset(); + + yield setGlobal("foo", 2); + observerArgsOK(args.foo, [[null, "foo", 2]]); + observerArgsOK(args.null, [[null, "foo", 2]]); + observerArgsOK(args.bar, []); + args.reset(); + }, + + function observerForName_remove() { + yield set("a.com", "foo", 1); + yield setGlobal("foo", 2); + + let args = on("Removed", ["foo", null, "bar"]); + yield cps.removeByDomainAndName("a.com", "bogus", null, makeCallback()); + observerArgsOK(args.foo, []); + observerArgsOK(args.null, []); + observerArgsOK(args.bar, []); + args.reset(); + + yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback()); + observerArgsOK(args.foo, [["a.com", "foo"]]); + observerArgsOK(args.null, [["a.com", "foo"]]); + observerArgsOK(args.bar, []); + args.reset(); + + yield cps.removeGlobal("foo", null, makeCallback()); + observerArgsOK(args.foo, [[null, "foo"]]); + observerArgsOK(args.null, [[null, "foo"]]); + observerArgsOK(args.bar, []); + args.reset(); + }, + + function observerForName_removeByDomain() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "bar", 2); + yield setGlobal("foo", 3); + + let args = on("Removed", ["foo", null, "bar"]); + yield cps.removeByDomain("bogus", null, makeCallback()); + observerArgsOK(args.foo, []); + observerArgsOK(args.null, []); + observerArgsOK(args.bar, []); + args.reset(); + + yield cps.removeBySubdomain("a.com", null, makeCallback()); + observerArgsOK(args.foo, [["a.com", "foo"]]); + observerArgsOK(args.null, [["a.com", "foo"], ["b.a.com", "bar"]]); + observerArgsOK(args.bar, [["b.a.com", "bar"]]); + args.reset(); + + yield cps.removeAllGlobals(null, makeCallback()); + observerArgsOK(args.foo, [[null, "foo"]]); + observerArgsOK(args.null, [[null, "foo"]]); + observerArgsOK(args.bar, []); + args.reset(); + }, + + function observerForName_removeAllDomains() { + yield set("a.com", "foo", 1); + yield setGlobal("foo", 2); + yield set("b.com", "bar", 3); + + let args = on("Removed", ["foo", null, "bar"]); + yield cps.removeAllDomains(null, makeCallback()); + observerArgsOK(args.foo, [["a.com", "foo"]]); + observerArgsOK(args.null, [["a.com", "foo"], ["b.com", "bar"]]); + observerArgsOK(args.bar, [["b.com", "bar"]]); + args.reset(); + }, + + function observerForName_removeByName() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + + let args = on("Removed", ["foo", null, "bar"]); + yield cps.removeByName("bogus", null, makeCallback()); + observerArgsOK(args.foo, []); + observerArgsOK(args.null, []); + observerArgsOK(args.bar, []); + args.reset(); + + yield cps.removeByName("foo", null, makeCallback()); + observerArgsOK(args.foo, [["a.com", "foo"], [null, "foo"]]); + observerArgsOK(args.null, [["a.com", "foo"], [null, "foo"]]); + observerArgsOK(args.bar, []); + args.reset(); + }, + + function removeObserverForName() { + let args = on("Set", ["foo", null, "bar"]); + + cps.removeObserverForName("foo", args.foo.observer); + yield set("a.com", "foo", 1); + observerArgsOK(args.foo, []); + observerArgsOK(args.null, [["a.com", "foo", 1]]); + observerArgsOK(args.bar, []); + args.reset(); + + cps.removeObserverForName(null, args.null.observer); + yield set("a.com", "foo", 2); + observerArgsOK(args.foo, []); + observerArgsOK(args.null, []); + observerArgsOK(args.bar, []); + args.reset(); + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_remove.js b/toolkit/components/contentprefs/tests/unit_cps2/test_remove.js new file mode 100644 index 000000000000..c5fe724f6e7e --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_remove.js @@ -0,0 +1,194 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function nonexistent() { + yield set("a.com", "foo", 1); + yield setGlobal("foo", 2); + + yield cps.removeByDomainAndName("a.com", "bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + + yield cps.removeBySubdomainAndName("a.com", "bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + + yield cps.removeGlobal("bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + + yield cps.removeByDomainAndName("bogus", "bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + }, + + function isomorphicDomains() { + yield set("a.com", "foo", 1); + yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback()); + yield dbOK([]); + yield getOK(["a.com", "foo"], undefined); + + yield set("a.com", "foo", 2); + yield cps.removeByDomainAndName("http://a.com/huh", "foo", null, + makeCallback()); + yield dbOK([]); + yield getOK(["a.com", "foo"], undefined); + }, + + function names() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + + yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback()); + yield dbOK([ + ["a.com", "bar", 2], + [null, "foo", 3], + [null, "bar", 4], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], 4); + + yield cps.removeGlobal("foo", null, makeCallback()); + yield dbOK([ + ["a.com", "bar", 2], + [null, "bar", 4], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], 4); + + yield cps.removeByDomainAndName("a.com", "bar", null, makeCallback()); + yield dbOK([ + [null, "bar", 4], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], 4); + + yield cps.removeGlobal("bar", null, makeCallback()); + yield dbOK([ + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], undefined); + }, + + function subdomains() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "foo", 2); + yield cps.removeByDomainAndName("a.com", "foo", null, makeCallback()); + yield dbOK([ + ["b.a.com", "foo", 2], + ]); + yield getSubdomainsOK(["a.com", "foo"], [["b.a.com", 2]]); + yield getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + + yield set("a.com", "foo", 3); + yield cps.removeBySubdomainAndName("a.com", "foo", null, makeCallback()); + yield dbOK([ + ]); + yield getSubdomainsOK(["a.com", "foo"], []); + yield getSubdomainsOK(["b.a.com", "foo"], []); + + yield set("a.com", "foo", 4); + yield set("b.a.com", "foo", 5); + yield cps.removeByDomainAndName("b.a.com", "foo", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 4], + ]); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + yield getSubdomainsOK(["b.a.com", "foo"], []); + + yield set("b.a.com", "foo", 6); + yield cps.removeBySubdomainAndName("b.a.com", "foo", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 4], + ]); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + yield getSubdomainsOK(["b.a.com", "foo"], []); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield setGlobal("qux", 5); + yield set("b.com", "foo", 6); + yield set("b.com", "bar", 7); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 8, context); + yield setGlobal("foo", 9, context); + yield cps.removeByDomainAndName("a.com", "foo", context, makeCallback()); + yield cps.removeGlobal("foo", context, makeCallback()); + yield cps.removeGlobal("qux", context, makeCallback()); + yield cps.removeByDomainAndName("b.com", "foo", context, makeCallback()); + yield dbOK([ + ["a.com", "bar", 2], + [null, "bar", 4], + ["b.com", "bar", 7], + ]); + yield getOK(["a.com", "foo", context], undefined); + yield getOK(["a.com", "bar", context], 2); + yield getGlobalOK(["foo", context], undefined); + yield getGlobalOK(["bar", context], 4); + yield getGlobalOK(["qux", context], undefined); + yield getOK(["b.com", "foo", context], undefined); + yield getOK(["b.com", "bar", context], 7); + + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], 4); + yield getGlobalOK(["qux"], undefined); + yield getOK(["b.com", "foo"], undefined); + yield getOK(["b.com", "bar"], 7); + }, + + function erroneous() { + do_check_throws(function () cps.removeByDomainAndName(null, "foo", null)); + do_check_throws(function () cps.removeByDomainAndName("", "foo", null)); + do_check_throws(function () cps.removeByDomainAndName("a.com", "foo", null, + "bogus")); + do_check_throws(function () cps.removeBySubdomainAndName(null, "foo", + null)); + do_check_throws(function () cps.removeBySubdomainAndName("", "foo", null)); + do_check_throws(function () cps.removeBySubdomainAndName("a.com", "foo", + null, "bogus")); + do_check_throws(function () cps.removeGlobal("", null)); + do_check_throws(function () cps.removeGlobal(null, null)); + do_check_throws(function () cps.removeGlobal("foo", null, "bogus")); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js new file mode 100644 index 000000000000..63f3fe1af128 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeAllDomains.js @@ -0,0 +1,73 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function nonexistent() { + yield setGlobal("foo", 1); + yield cps.removeAllDomains(null, makeCallback()); + yield dbOK([ + [null, "foo", 1], + ]); + yield getGlobalOK(["foo"], 1); + }, + + function domains() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + yield set("b.com", "bar", 6); + + yield cps.removeAllDomains(null, makeCallback()); + yield dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], 4); + yield getOK(["b.com", "foo"], undefined); + yield getOK(["b.com", "bar"], undefined); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 6, context); + yield setGlobal("foo", 7, context); + yield cps.removeAllDomains(context, makeCallback()); + yield dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ]); + yield getOK(["a.com", "foo", context], undefined); + yield getOK(["a.com", "bar", context], undefined); + yield getGlobalOK(["foo", context], 7); + yield getGlobalOK(["bar", context], 4); + yield getOK(["b.com", "foo", context], undefined); + + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], 4); + yield getOK(["b.com", "foo"], undefined); + }, + + function erroneous() { + do_check_throws(function () cps.removeAllDomains(null, "bogus")); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js new file mode 100644 index 000000000000..1e4adea5a94e --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByDomain.js @@ -0,0 +1,162 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function nonexistent() { + yield set("a.com", "foo", 1); + yield setGlobal("foo", 2); + + yield cps.removeByDomain("bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + + yield cps.removeBySubdomain("bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + }, + + function isomorphicDomains() { + yield set("a.com", "foo", 1); + yield cps.removeByDomain("a.com", null, makeCallback()); + yield dbOK([]); + yield getOK(["a.com", "foo"], undefined); + + yield set("a.com", "foo", 2); + yield cps.removeByDomain("http://a.com/huh", null, makeCallback()); + yield dbOK([]); + yield getOK(["a.com", "foo"], undefined); + }, + + function domains() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + yield set("b.com", "bar", 6); + + yield cps.removeByDomain("a.com", null, makeCallback()); + yield dbOK([ + [null, "foo", 3], + [null, "bar", 4], + ["b.com", "foo", 5], + ["b.com", "bar", 6], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], 4); + yield getOK(["b.com", "foo"], 5); + yield getOK(["b.com", "bar"], 6); + + yield cps.removeAllGlobals(null, makeCallback()); + yield dbOK([ + ["b.com", "foo", 5], + ["b.com", "bar", 6], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], undefined); + yield getOK(["b.com", "foo"], 5); + yield getOK(["b.com", "bar"], 6); + + yield cps.removeByDomain("b.com", null, makeCallback()); + yield dbOK([ + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], undefined); + yield getOK(["b.com", "foo"], undefined); + yield getOK(["b.com", "bar"], undefined); + }, + + function subdomains() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "foo", 2); + yield cps.removeByDomain("a.com", null, makeCallback()); + yield dbOK([ + ["b.a.com", "foo", 2], + ]); + yield getSubdomainsOK(["a.com", "foo"], [["b.a.com", 2]]); + yield getSubdomainsOK(["b.a.com", "foo"], [["b.a.com", 2]]); + + yield set("a.com", "foo", 3); + yield cps.removeBySubdomain("a.com", null, makeCallback()); + yield dbOK([ + ]); + yield getSubdomainsOK(["a.com", "foo"], []); + yield getSubdomainsOK(["b.a.com", "foo"], []); + + yield set("a.com", "foo", 4); + yield set("b.a.com", "foo", 5); + yield cps.removeByDomain("b.a.com", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 4], + ]); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + yield getSubdomainsOK(["b.a.com", "foo"], []); + + yield set("b.a.com", "foo", 6); + yield cps.removeBySubdomain("b.a.com", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 4], + ]); + yield getSubdomainsOK(["a.com", "foo"], [["a.com", 4]]); + yield getSubdomainsOK(["b.a.com", "foo"], []); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 6, context); + yield setGlobal("foo", 7, context); + yield cps.removeByDomain("a.com", context, makeCallback()); + yield cps.removeAllGlobals(context, makeCallback()); + yield dbOK([ + ["b.com", "foo", 5], + ]); + yield getOK(["a.com", "foo", context], undefined); + yield getOK(["a.com", "bar", context], undefined); + yield getGlobalOK(["foo", context], undefined); + yield getGlobalOK(["bar", context], undefined); + yield getOK(["b.com", "foo", context], 5); + + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], undefined); + yield getOK(["b.com", "foo"], 5); + }, + + function erroneous() { + do_check_throws(function () cps.removeByDomain(null, null)); + do_check_throws(function () cps.removeByDomain("", null)); + do_check_throws(function () cps.removeByDomain("a.com", null, "bogus")); + do_check_throws(function () cps.removeBySubdomain(null, null)); + do_check_throws(function () cps.removeBySubdomain("", null)); + do_check_throws(function () cps.removeBySubdomain("a.com", null, "bogus")); + do_check_throws(function () cps.removeAllGlobals(null, "bogus")); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js new file mode 100644 index 000000000000..039c803fd8b3 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_removeByName.js @@ -0,0 +1,85 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function nonexistent() { + yield set("a.com", "foo", 1); + yield setGlobal("foo", 2); + + yield cps.removeByName("bogus", null, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getGlobalOK(["foo"], 2); + }, + + function names() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + yield set("b.com", "bar", 6); + + yield cps.removeByName("foo", null, makeCallback()); + yield dbOK([ + ["a.com", "bar", 2], + [null, "bar", 4], + ["b.com", "bar", 6], + ]); + yield getOK(["a.com", "foo"], undefined); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], undefined); + yield getGlobalOK(["bar"], 4); + yield getOK(["b.com", "foo"], undefined); + yield getOK(["b.com", "bar"], 6); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + yield set("b.com", "bar", 6); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 7, context); + yield setGlobal("foo", 8, context); + yield set("b.com", "bar", 9, context); + yield cps.removeByName("bar", context, makeCallback()); + yield dbOK([ + ["a.com", "foo", 1], + [null, "foo", 3], + ["b.com", "foo", 5], + ]); + yield getOK(["a.com", "foo", context], 7); + yield getOK(["a.com", "bar", context], undefined); + yield getGlobalOK(["foo", context], 8); + yield getGlobalOK(["bar", context], undefined); + yield getOK(["b.com", "foo", context], 5); + yield getOK(["b.com", "bar", context], undefined); + + yield getOK(["a.com", "foo"], 1); + yield getOK(["a.com", "bar"], undefined); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], undefined); + yield getOK(["b.com", "foo"], 5); + yield getOK(["b.com", "bar"], undefined); + }, + + function erroneous() { + do_check_throws(function () cps.removeByName("", null)); + do_check_throws(function () cps.removeByName(null, null)); + do_check_throws(function () cps.removeByName("foo", null, "bogus")); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_service.js b/toolkit/components/contentprefs/tests/unit_cps2/test_service.js new file mode 100644 index 000000000000..75292063e80c --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_service.js @@ -0,0 +1,12 @@ +/* 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/. */ + +function run_test() { + let serv = Cc["@mozilla.org/content-pref/service;1"]. + getService(Ci.nsIContentPrefService2); + do_check_eq(serv.QueryInterface(Ci.nsIContentPrefService2), serv); + do_check_eq(serv.QueryInterface(Ci.nsISupports), serv); + let val = serv.QueryInterface(Ci.nsIContentPrefService); + do_check_true(val instanceof Ci.nsIContentPrefService); +} diff --git a/toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js b/toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js new file mode 100644 index 000000000000..47c1e4fb8807 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/test_setGet.js @@ -0,0 +1,138 @@ +/* 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/. */ + +function run_test() { + runAsyncTests(tests); +} + +let tests = [ + + function get_nonexistent() { + yield getOK(["a.com", "foo"], undefined); + yield getGlobalOK(["foo"], undefined); + }, + + function isomorphicDomains() { + yield set("a.com", "foo", 1); + yield dbOK([ + ["a.com", "foo", 1], + ]); + yield getOK(["a.com", "foo"], 1); + yield getOK(["http://a.com/huh", "foo"], 1, "a.com"); + + yield set("http://a.com/huh", "foo", 2); + yield dbOK([ + ["a.com", "foo", 2], + ]); + yield getOK(["a.com", "foo"], 2); + yield getOK(["http://a.com/yeah", "foo"], 2, "a.com"); + }, + + function names() { + yield set("a.com", "foo", 1); + yield dbOK([ + ["a.com", "foo", 1], + ]); + yield getOK(["a.com", "foo"], 1); + + yield set("a.com", "bar", 2); + yield dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getOK(["a.com", "bar"], 2); + + yield setGlobal("foo", 3); + yield dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + [null, "foo", 3], + ]); + yield getOK(["a.com", "foo"], 1); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], 3); + + yield setGlobal("bar", 4); + yield dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + [null, "foo", 3], + [null, "bar", 4], + ]); + yield getOK(["a.com", "foo"], 1); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], 4); + }, + + function subdomains() { + yield set("a.com", "foo", 1); + yield set("b.a.com", "foo", 2); + yield dbOK([ + ["a.com", "foo", 1], + ["b.a.com", "foo", 2], + ]); + yield getOK(["a.com", "foo"], 1); + yield getOK(["b.a.com", "foo"], 2); + }, + + function privateBrowsing() { + yield set("a.com", "foo", 1); + yield set("a.com", "bar", 2); + yield setGlobal("foo", 3); + yield setGlobal("bar", 4); + yield set("b.com", "foo", 5); + + let context = { usePrivateBrowsing: true }; + yield set("a.com", "foo", 6, context); + yield setGlobal("foo", 7, context); + yield dbOK([ + ["a.com", "foo", 1], + ["a.com", "bar", 2], + [null, "foo", 3], + [null, "bar", 4], + ["b.com", "foo", 5], + ]); + yield getOK(["a.com", "foo", context], 6, "a.com"); + yield getOK(["a.com", "bar", context], 2); + yield getGlobalOK(["foo", context], 7); + yield getGlobalOK(["bar", context], 4); + yield getOK(["b.com", "foo", context], 5); + + yield getOK(["a.com", "foo"], 1); + yield getOK(["a.com", "bar"], 2); + yield getGlobalOK(["foo"], 3); + yield getGlobalOK(["bar"], 4); + yield getOK(["b.com", "foo"], 5); + }, + + function set_erroneous() { + do_check_throws(function () cps.set(null, "foo", 1, null)); + do_check_throws(function () cps.set("", "foo", 1, null)); + do_check_throws(function () cps.set("a.com", "", 1, null)); + do_check_throws(function () cps.set("a.com", null, 1, null)); + do_check_throws(function () cps.set("a.com", "foo", undefined, null)); + do_check_throws(function () cps.set("a.com", "foo", 1, null, "bogus")); + do_check_throws(function () cps.setGlobal("", 1, null)); + do_check_throws(function () cps.setGlobal(null, 1, null)); + do_check_throws(function () cps.setGlobal("foo", undefined, null)); + do_check_throws(function () cps.setGlobal("foo", 1, null, "bogus")); + yield true; + }, + + function get_erroneous() { + do_check_throws(function () cps.getByDomainAndName(null, "foo", null, {})); + do_check_throws(function () cps.getByDomainAndName("", "foo", null, {})); + do_check_throws(function () cps.getByDomainAndName("a.com", "", null, {})); + do_check_throws(function () + cps.getByDomainAndName("a.com", null, null, {})); + do_check_throws(function () + cps.getByDomainAndName("a.com", "foo", null, null)); + do_check_throws(function () cps.getGlobal("", null, {})); + do_check_throws(function () cps.getGlobal(null, null, {})); + do_check_throws(function () cps.getGlobal("foo", null, null)); + yield true; + }, +]; diff --git a/toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini b/toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini new file mode 100644 index 000000000000..0fad2a9f2a94 --- /dev/null +++ b/toolkit/components/contentprefs/tests/unit_cps2/xpcshell.ini @@ -0,0 +1,14 @@ +[DEFAULT] +head = head.js +tail = + +[test_service.js] +[test_setGet.js] +[test_getSubdomains.js] +[test_remove.js] +[test_removeByDomain.js] +[test_removeAllDomains.js] +[test_removeByName.js] +[test_getCached.js] +[test_getCachedSubdomains.js] +[test_observers.js] From 2708f65d81eb1213a5af75a0d44d030685323ff3 Mon Sep 17 00:00:00 2001 From: Chiajung Hung Date: Thu, 22 Nov 2012 10:40:57 +0800 Subject: [PATCH 059/217] Bug 805939: Wait for shadow trees to update orientation before recompositing. r=cjones --- b2g/app/b2g.js | 3 + dom/base/ScreenOrientation.h | 2 - dom/ipc/Makefile.in | 1 + dom/ipc/PBrowser.ipdl | 4 +- dom/ipc/TabChild.cpp | 4 +- dom/ipc/TabChild.h | 5 +- dom/ipc/TabParent.cpp | 6 +- gfx/layers/Makefile.in | 1 + gfx/layers/basic/BasicLayerManager.cpp | 13 +++- gfx/layers/ipc/CompositorParent.cpp | 101 +++++++++++++++++++++---- gfx/layers/ipc/CompositorParent.h | 5 ++ gfx/layers/ipc/PLayers.ipdl | 4 + gfx/layers/ipc/ShadowLayers.cpp | 16 +++- gfx/layers/ipc/ShadowLayers.h | 5 +- gfx/layers/opengl/LayerManagerOGL.cpp | 18 ++++- gfx/layers/opengl/LayerManagerOGL.h | 3 + gfx/thebes/gfxPlatform.cpp | 39 ++++++++++ gfx/thebes/gfxPlatform.h | 6 ++ 18 files changed, 204 insertions(+), 32 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index baea1419827b..1e37734c7406 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -615,3 +615,6 @@ pref("memory.free_dirty_pages", true); // UAProfile settings pref("wap.UAProf.url", ""); pref("wap.UAProf.tagname", "x-wap-profile"); + +// Wait up to this much milliseconds when orientation changed +pref("layers.orientation.sync.timeout", 1000); diff --git a/dom/base/ScreenOrientation.h b/dom/base/ScreenOrientation.h index 3ff87d2c42b4..fe218b200757 100644 --- a/dom/base/ScreenOrientation.h +++ b/dom/base/ScreenOrientation.h @@ -5,8 +5,6 @@ #ifndef mozilla_dom_ScreenOrientation_h #define mozilla_dom_ScreenOrientation_h -#include "ipc/IPCMessageUtils.h" - namespace mozilla { namespace dom { diff --git a/dom/ipc/Makefile.in b/dom/ipc/Makefile.in index ce6575c1c093..2caa5acc6ab0 100644 --- a/dom/ipc/Makefile.in +++ b/dom/ipc/Makefile.in @@ -94,6 +94,7 @@ LOCAL_INCLUDES += \ -I$(topsrcdir)/widget/xpwidgets \ -I$(topsrcdir)/dom/bluetooth \ -I$(topsrcdir)/dom/bluetooth/ipc \ + -I$(topsrcdir)/hal \ $(NULL) DEFINES += -DBIN_SUFFIX='"$(BIN_SUFFIX)"' diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 8823144f404d..a7270b93d557 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -20,6 +20,7 @@ include "gfxMatrix.h"; include "FrameMetrics.h"; include "IPC/nsGUIEventIPC.h"; include "mozilla/dom/TabMessageUtils.h"; +include "mozilla/dom/ScreenOrientation.h"; include "mozilla/dom/PermissionMessageUtils.h"; include "mozilla/layout/RenderFrameUtils.h"; @@ -47,6 +48,7 @@ using nsSelectionEvent; using nsTextEvent; using nsTouchEvent; using RemoteDOMEvent; +using mozilla::dom::ScreenOrientation; namespace mozilla { namespace dom { @@ -288,7 +290,7 @@ child: LoadURL(nsCString uri); - UpdateDimensions(nsRect rect, nsIntSize size) compress; + UpdateDimensions(nsRect rect, nsIntSize size, ScreenOrientation orientation) compress; UpdateFrame(FrameMetrics frame) compress; diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index d3e5c0c96784..eb480234d384 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -168,6 +168,7 @@ TabChild::TabChild(const TabContext& aContext, uint32_t aChromeFlags) , mNotified(false) , mContentDocumentIsDisplayed(false) , mTriedBrowserInit(false) + , mOrientation(eScreenOrientation_PortraitPrimary) { printf("creating %d!\n", NS_IsMainThread()); } @@ -1119,7 +1120,7 @@ TabChild::RecvShow(const nsIntSize& size) } bool -TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size) +TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size, const ScreenOrientation& orientation) { if (!mRemoteFrame) { return true; @@ -1130,6 +1131,7 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size) mOuterRect.width = rect.width; mOuterRect.height = rect.height; + mOrientation = orientation; mInnerSize = size; mWidget->Resize(0, 0, size.width, size.height, true); diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 707d07fe7bdd..e565c1f1ceb8 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -197,7 +197,7 @@ public: virtual bool RecvLoadURL(const nsCString& uri); virtual bool RecvShow(const nsIntSize& size); - virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size); + virtual bool RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size, const ScreenOrientation& orientation); virtual bool RecvUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); virtual bool RecvHandleDoubleTap(const nsIntPoint& aPoint); virtual bool RecvHandleSingleTap(const nsIntPoint& aPoint); @@ -291,6 +291,8 @@ public: gfxSize GetZoom() { return mLastMetrics.mZoom; } + ScreenOrientation GetOrientation() { return mOrientation; } + void SetBackgroundColor(const nscolor& aColor); void NotifyPainted(); @@ -417,6 +419,7 @@ private: bool mContentDocumentIsDisplayed; bool mTriedBrowserInit; nsString mAppType; + ScreenOrientation mOrientation; DISALLOW_EVIL_CONSTRUCTORS(TabChild); }; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 63b883d555b1..3a490b06084a 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -51,6 +51,7 @@ #include "nsThreadUtils.h" #include "StructuredCloneUtils.h" #include "TabChild.h" +#include "Hal.h" using namespace mozilla::dom; using namespace mozilla::ipc; @@ -256,7 +257,10 @@ TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size) if (mIsDestroyed) { return; } - unused << SendUpdateDimensions(rect, size); + hal::ScreenConfiguration config; + hal::GetCurrentScreenConfiguration(&config); + + unused << SendUpdateDimensions(rect, size, config.orientation()); if (RenderFrameParent* rfp = GetRenderFrame()) { rfp->NotifyDimensionsChanged(size.width, size.height); } diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 46e87e8023d4..b83765ec15ed 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -199,6 +199,7 @@ include $(topsrcdir)/ipc/chromium/chromium-config.mk LOCAL_INCLUDES += \ -I$(topsrcdir)/content/events/src \ + -I$(topsrcdir)/hal \ -I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright \ -I$(ANDROID_SOURCE)/frameworks/base/include/media/stagefright/openmax \ $(NULL) diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp index ca13ba773e60..2366efa65415 100644 --- a/gfx/layers/basic/BasicLayerManager.cpp +++ b/gfx/layers/basic/BasicLayerManager.cpp @@ -15,6 +15,7 @@ #include "nsXULAppAPI.h" #include "RenderTrace.h" #include "sampler.h" +#include "Hal.h" #define PIXMAN_DONT_DEFINE_STDINT #include "pixman.h" @@ -1094,7 +1095,17 @@ BasicShadowLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) // don't signal a new transaction to ShadowLayerForwarder. Carry on adding // to the previous transaction. if (HasShadowManager()) { - ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation); + ScreenOrientation orientation; + nsIntRect clientBounds; + if (TabChild* window = mWidget->GetOwningTabChild()) { + orientation = window->GetOrientation(); + } else { + hal::ScreenConfiguration currentConfig; + hal::GetCurrentScreenConfiguration(¤tConfig); + orientation = currentConfig.orientation(); + } + mWidget->GetClientBounds(clientBounds); + ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation, clientBounds, orientation); // If we're drawing on behalf of a context with async pan/zoom // enabled, then the entire buffer of thebes layers might be diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 13caa8555d29..420563ec8e91 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -34,9 +34,14 @@ #include "AnimationCommon.h" #include "nsAnimationManager.h" #include "TiledLayerBuffer.h" +#include "gfxPlatform.h" +#include "mozilla/dom/ScreenOrientation.h" +#include "mozilla/AutoRestore.h" using namespace base; +using namespace mozilla; using namespace mozilla::ipc; +using namespace mozilla::dom; using namespace std; namespace mozilla { @@ -60,6 +65,7 @@ static MessageLoop* sCompositorLoop = nullptr; struct LayerTreeState { nsRefPtr mRoot; nsRefPtr mController; + TargetConfig mTargetConfig; }; static uint8_t sPanZoomUserDataKey; @@ -172,6 +178,8 @@ CompositorParent::CompositorParent(nsIWidget* aWidget, , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight) , mPauseCompositionMonitor("PauseCompositionMonitor") , mResumeCompositionMonitor("ResumeCompositionMonitor") + , mForceCompositionTask(nullptr) + , mOverrideComposeReadiness(false) { NS_ABORT_IF_FALSE(sCompositorThread != nullptr || sCompositorThreadID, "The compositor thread must be Initialized before instanciating a COmpositorParent."); @@ -346,6 +354,14 @@ CompositorParent::ResumeComposition() lock.NotifyAll(); } +void +CompositorParent::ForceComposition() +{ + // Cancel the orientation changed state to force composition + mForceCompositionTask = nullptr; + ScheduleRenderOnCompositorThread(); +} + void CompositorParent::SetEGLSurfaceSize(int width, int height) { @@ -469,12 +485,15 @@ public: * guaranteed by this helper only being used during the drawing * phase. */ - AutoResolveRefLayers(Layer* aRoot) : mRoot(aRoot) + AutoResolveRefLayers(Layer* aRoot, const TargetConfig& aConfig) : mRoot(aRoot), mTargetConfig(aConfig), mReadyForCompose(true) { WalkTheTree(mRoot, nullptr); } ~AutoResolveRefLayers() { WalkTheTree(mRoot, nullptr); } + bool IsReadyForCompose() + { return mReadyForCompose; } + private: enum Op { Resolve, Detach }; template @@ -482,20 +501,29 @@ private: { if (RefLayer* ref = aLayer->AsRefLayer()) { if (const LayerTreeState* state = GetIndirectShadowTree(ref->GetReferentId())) { - if (Layer* referent = state->mRoot) { - if (OP == Resolve) { - ref->ConnectReferentLayer(referent); - if (AsyncPanZoomController* apzc = state->mController) { - referent->SetUserData(&sPanZoomUserDataKey, - new PanZoomUserData(apzc)); - } else { - CompensateForContentScrollOffset(ref, referent); - } - } else { - ref->DetachReferentLayer(referent); - referent->RemoveUserData(&sPanZoomUserDataKey); + Layer* referent = state->mRoot; + + if (!ref->GetVisibleRegion().IsEmpty()) { + ScreenOrientation chromeOrientation = mTargetConfig.orientation(); + ScreenOrientation contentOrientation = state->mTargetConfig.orientation(); + if (!IsSameDimension(chromeOrientation, contentOrientation) && + ContentMightReflowOnOrientationChange(mTargetConfig.clientBounds())) { + mReadyForCompose = false; } } + + if (OP == Resolve) { + ref->ConnectReferentLayer(referent); + if (AsyncPanZoomController* apzc = state->mController) { + referent->SetUserData(&sPanZoomUserDataKey, + new PanZoomUserData(apzc)); + } else { + CompensateForContentScrollOffset(ref, referent); + } + } else { + ref->DetachReferentLayer(referent); + referent->RemoveUserData(&sPanZoomUserDataKey); + } } } for (Layer* child = aLayer->GetFirstChild(); @@ -529,7 +557,19 @@ private: aContainer->AsShadowLayer()->SetShadowTransform(m); } + bool IsSameDimension(ScreenOrientation o1, ScreenOrientation o2) { + bool isO1portrait = (o1 == eScreenOrientation_PortraitPrimary || o1 == eScreenOrientation_PortraitSecondary); + bool isO2portrait = (o2 == eScreenOrientation_PortraitPrimary || o2 == eScreenOrientation_PortraitSecondary); + return !(isO1portrait ^ isO2portrait); + } + + bool ContentMightReflowOnOrientationChange(nsIntRect& rect) { + return rect.width != rect.height; + } + Layer* mRoot; + TargetConfig mTargetConfig; + bool mReadyForCompose; AutoResolveRefLayers(const AutoResolveRefLayers&) MOZ_DELETE; AutoResolveRefLayers& operator=(const AutoResolveRefLayers&) MOZ_DELETE; @@ -549,7 +589,15 @@ CompositorParent::Composite() } Layer* layer = mLayerManager->GetRoot(); - AutoResolveRefLayers resolve(layer); + AutoResolveRefLayers resolve(layer, mTargetConfig); + if (mForceCompositionTask && !mOverrideComposeReadiness) { + if (!resolve.IsReadyForCompose()) { + return; + } else { + mForceCompositionTask->Cancel(); + mForceCompositionTask = nullptr; + } + } bool requestNextFrame = TransformShadowTree(mLastCompose); if (requestNextFrame) { @@ -578,9 +626,13 @@ CompositorParent::Composite() void CompositorParent::ComposeToTarget(gfxContext* aTarget) { + AutoRestore override(mOverrideComposeReadiness); + mOverrideComposeReadiness = true; + if (!CanComposite()) { return; } + mLayerManager->BeginTransactionWithTarget(aTarget); // Since CanComposite() is true, Composite() must end the layers txn // we opened above. @@ -990,6 +1042,22 @@ CompositorParent::ShadowLayersUpdated(ShadowLayersParent* aLayerTree, const TargetConfig& aTargetConfig, bool isFirstPaint) { + if (!isFirstPaint && !mIsFirstPaint && mTargetConfig.orientation() != aTargetConfig.orientation()) { + if (mForceCompositionTask != NULL) { + mForceCompositionTask->Cancel(); + } + mForceCompositionTask = NewRunnableMethod(this, &CompositorParent::ForceComposition); + ScheduleTask(mForceCompositionTask, gfxPlatform::GetPlatform()->GetOrientationSyncMillis()); + } + + // Instruct the LayerManager to update its render bounds now. Since all the orientation + // change, dimension change would be done at the stage, update the size here is free of + // race condition. + if (LAYERS_OPENGL == mLayerManager->GetBackendType()) { + LayerManagerOGL* lm = static_cast(mLayerManager.get()); + lm->UpdateRenderBounds(aTargetConfig.clientBounds()); + } + mTargetConfig = aTargetConfig; mIsFirstPaint = mIsFirstPaint || isFirstPaint; mLayersUpdated = true; @@ -1252,9 +1320,10 @@ CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess) } static void -UpdateIndirectTree(uint64_t aId, Layer* aRoot, bool isFirstPaint) +UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig, bool isFirstPaint) { sIndirectLayerTrees[aId].mRoot = aRoot; + sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig; if (ContainerLayer* root = aRoot->AsContainerLayer()) { if (AsyncPanZoomController* apzc = sIndirectLayerTrees[aId].mController) { apzc->NotifyLayersUpdated(root->GetFrameMetrics(), isFirstPaint); @@ -1321,7 +1390,7 @@ CrossProcessCompositorParent::ShadowLayersUpdated( if (shadowRoot) { SetShadowProperties(shadowRoot); } - UpdateIndirectTree(id, shadowRoot, isFirstPaint); + UpdateIndirectTree(id, shadowRoot, aTargetConfig, isFirstPaint); sCurrentCompositor->NotifyShadowTreeTransaction(); } diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 16de53080ce5..f98615b5d449 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -58,6 +58,7 @@ class CompositorParent : public PCompositorParent, public ShadowLayersManager { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorParent) + public: CompositorParent(nsIWidget* aWidget, bool aRenderToEGLSurface = false, @@ -180,6 +181,7 @@ private: void PauseComposition(); void ResumeComposition(); void ResumeCompositionAndResize(int width, int height); + void ForceComposition(); // Sample transforms for layer trees. Return true to request // another animation frame. @@ -291,6 +293,9 @@ private: uint64_t mCompositorID; + bool mOverrideComposeReadiness; + CancelableTask* mForceCompositionTask; + DISALLOW_EVIL_CONSTRUCTORS(CompositorParent); }; diff --git a/gfx/layers/ipc/PLayers.ipdl b/gfx/layers/ipc/PLayers.ipdl index 6ebfde9ac499..514cd14a0f46 100644 --- a/gfx/layers/ipc/PLayers.ipdl +++ b/gfx/layers/ipc/PLayers.ipdl @@ -14,6 +14,7 @@ include protocol PRenderFrame; include "gfxipc/ShadowLayerUtils.h"; include "mozilla/WidgetUtils.h"; include "mozilla/TimeStamp.h"; +include "mozilla/dom/ScreenOrientation.h"; include "nsCSSProperty.h"; using gfxPoint3D; @@ -24,6 +25,7 @@ using mozilla::TimeDuration; using mozilla::TimeStamp; using mozilla::ScreenRotation; using nsCSSProperty; +using mozilla::dom::ScreenOrientation; /** * The layers protocol is spoken between thread contexts that manage @@ -39,6 +41,8 @@ namespace layers { struct TargetConfig { nsIntRect naturalBounds; ScreenRotation rotation; + nsIntRect clientBounds; + ScreenOrientation orientation; }; // Create a shadow layer for |layer| diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 31602eaa76cb..68ce78a465cb 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -25,6 +25,7 @@ using namespace mozilla::ipc; using namespace mozilla::gl; +using namespace mozilla::dom; namespace mozilla { namespace layers { @@ -42,7 +43,8 @@ public: , mRotationChanged(false) {} - void Begin(const nsIntRect& aTargetBounds, ScreenRotation aRotation) + void Begin(const nsIntRect& aTargetBounds, ScreenRotation aRotation, + const nsIntRect& aClientBounds, ScreenOrientation aOrientation) { mOpen = true; mTargetBounds = aTargetBounds; @@ -50,6 +52,8 @@ public: mRotationChanged = true; } mTargetRotation = aRotation; + mClientBounds = aClientBounds; + mTargetOrientation = aOrientation; } void AddEdit(const Edit& aEdit) @@ -107,6 +111,8 @@ public: ShadowableLayerSet mMutants; nsIntRect mTargetBounds; ScreenRotation mTargetRotation; + nsIntRect mClientBounds; + ScreenOrientation mTargetOrientation; bool mSwapRequired; private: @@ -140,11 +146,13 @@ ShadowLayerForwarder::~ShadowLayerForwarder() void ShadowLayerForwarder::BeginTransaction(const nsIntRect& aTargetBounds, - ScreenRotation aRotation) + ScreenRotation aRotation, + const nsIntRect& aClientBounds, + ScreenOrientation aOrientation) { NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); NS_ABORT_IF_FALSE(mTxn->Finished(), "uncommitted txn?"); - mTxn->Begin(aTargetBounds, aRotation); + mTxn->Begin(aTargetBounds, aRotation, aClientBounds, aOrientation); } static PLayerChild* @@ -358,7 +366,7 @@ ShadowLayerForwarder::EndTransaction(InfallibleTArray* aReplies) cset.AppendElements(&mTxn->mPaints.front(), mTxn->mPaints.size()); } - TargetConfig targetConfig(mTxn->mTargetBounds, mTxn->mTargetRotation); + TargetConfig targetConfig(mTxn->mTargetBounds, mTxn->mTargetRotation, mTxn->mClientBounds, mTxn->mTargetOrientation); MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send...")); PlatformSyncBeforeUpdate(); diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h index 639db59ed3be..5d59accaa8c7 100644 --- a/gfx/layers/ipc/ShadowLayers.h +++ b/gfx/layers/ipc/ShadowLayers.h @@ -14,6 +14,7 @@ #include "ImageLayers.h" #include "mozilla/ipc/SharedMemory.h" #include "mozilla/WidgetUtils.h" +#include "mozilla/dom/ScreenOrientation.h" class gfxSharedImageSurface; @@ -116,7 +117,9 @@ public: * ShadowLayerManager. */ void BeginTransaction(const nsIntRect& aTargetBounds, - ScreenRotation aRotation); + ScreenRotation aRotation, + const nsIntRect& aClientBounds, + mozilla::dom::ScreenOrientation aOrientation); /** * The following methods may only be called after BeginTransaction() diff --git a/gfx/layers/opengl/LayerManagerOGL.cpp b/gfx/layers/opengl/LayerManagerOGL.cpp index fc6ec1a97d04..2f1463b262ff 100644 --- a/gfx/layers/opengl/LayerManagerOGL.cpp +++ b/gfx/layers/opengl/LayerManagerOGL.cpp @@ -1037,10 +1037,14 @@ LayerManagerOGL::Render() if (mIsRenderingToEGLSurface) { rect = nsIntRect(0, 0, mSurfaceSize.width, mSurfaceSize.height); } else { - // FIXME/bug XXXXXX this races with rotation changes on the main - // thread, and undoes all the care we take with layers txns being - // sent atomically with rotation changes - mWidget->GetClientBounds(rect); + rect = mRenderBounds; + // If render bounds is not updated explicitly, try to infer it from widget + if (rect.width == 0 || rect.height == 0) { + // FIXME/bug XXXXXX this races with rotation changes on the main + // thread, and undoes all the care we take with layers txns being + // sent atomically with rotation changes + mWidget->GetClientBounds(rect); + } } WorldTransformRect(rect); @@ -1265,6 +1269,12 @@ LayerManagerOGL::WorldTransformRect(nsIntRect& aRect) aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height()); } +void +LayerManagerOGL::UpdateRenderBounds(const nsIntRect& aRect) +{ + mRenderBounds = aRect; +} + void LayerManagerOGL::SetSurfaceSize(int width, int height) { diff --git a/gfx/layers/opengl/LayerManagerOGL.h b/gfx/layers/opengl/LayerManagerOGL.h index 5ff21c9f6972..4a54edf87a09 100644 --- a/gfx/layers/opengl/LayerManagerOGL.h +++ b/gfx/layers/opengl/LayerManagerOGL.h @@ -290,6 +290,8 @@ public: gfxMatrix& GetWorldTransform(void); void WorldTransformRect(nsIntRect& aRect); + void UpdateRenderBounds(const nsIntRect& aRect); + /** * Set the size of the surface we're rendering to. */ @@ -414,6 +416,7 @@ private: void *mThebesLayerCallbackData; gfxMatrix mWorldMatrix; nsAutoPtr mFPS; + nsIntRect mRenderBounds; #ifdef DEBUG // NB: only interesting when this is a purely compositing layer // manager. True after possibly onscreen layers have had their diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 2f5c52e02a75..c93516e86e64 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -174,7 +174,29 @@ FontPrefsObserver::Observe(nsISupports *aSubject, return NS_OK; } +class OrientationSyncPrefsObserver : public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER +}; +NS_IMPL_ISUPPORTS1(OrientationSyncPrefsObserver, nsIObserver) + +NS_IMETHODIMP +OrientationSyncPrefsObserver::Observe(nsISupports *aSubject, + const char *aTopic, + const PRUnichar *someData) +{ + if (!someData) { + NS_ERROR("orientation sync pref observer broken"); + return NS_ERROR_UNEXPECTED; + } + NS_ASSERTION(gfxPlatform::GetPlatform(), "the singleton instance has gone"); + gfxPlatform::GetPlatform()->OrientationSyncPrefsObserverChanged(); + + return NS_OK; +} // this needs to match the list of pref font.default.xx entries listed in all.js! // the order *must* match the order in eFontPrefLang @@ -338,6 +360,9 @@ gfxPlatform::Init() gPlatform->mFontPrefsObserver = new FontPrefsObserver(); Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs); + gPlatform->mOrientationSyncPrefsObserver = new OrientationSyncPrefsObserver(); + Preferences::AddStrongObserver(gPlatform->mOrientationSyncPrefsObserver, "layers.orientation.sync.timeout"); + gPlatform->mWorkAroundDriverBugs = Preferences::GetBool("gfx.work-around-driver-bugs", true); mozilla::Preferences::AddBoolVarCache(&gPlatform->mWidgetUpdateFlashing, @@ -359,6 +384,8 @@ gfxPlatform::Init() gPlatform->mRecorder = Factory::CreateEventRecorderForFile("browserrecording.aer"); Factory::SetGlobalEventRecorder(gPlatform->mRecorder); } + + gPlatform->mOrientationSyncMillis = Preferences::GetUint("layers.orientation.sync.timeout", (uint32_t)0); } void @@ -1699,3 +1726,15 @@ gfxPlatform::OptimalFormatForContent(gfxASurface::gfxContentType aContent) return gfxASurface::ImageFormatARGB32; } } + +void +gfxPlatform::OrientationSyncPrefsObserverChanged() +{ + mOrientationSyncMillis = Preferences::GetUint("layers.orientation.sync.timeout", (uint32_t)0); +} + +uint32_t +gfxPlatform::GetOrientationSyncMillis() const +{ + return mOrientationSyncMillis; +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 99badcf7b4dd..3a9b4772d43a 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -469,6 +469,8 @@ public: virtual void FontsPrefsChanged(const char *aPref); + void OrientationSyncPrefsObserverChanged(); + int32_t GetBidiNumeralOption(); /** @@ -495,6 +497,8 @@ public: bool WidgetUpdateFlashing() const { return mWidgetUpdateFlashing; } + uint32_t GetOrientationSyncMillis() const; + protected: gfxPlatform(); virtual ~gfxPlatform(); @@ -576,6 +580,7 @@ private: nsTArray mCJKPrefLangs; nsCOMPtr mSRGBOverrideObserver; nsCOMPtr mFontPrefsObserver; + nsCOMPtr mOrientationSyncPrefsObserver; // The preferred draw target backend to use for canvas mozilla::gfx::BackendType mPreferredCanvasBackend; @@ -589,6 +594,7 @@ private: mozilla::RefPtr mRecorder; bool mWidgetUpdateFlashing; + uint32_t mOrientationSyncMillis; }; #endif /* GFX_PLATFORM_H */ From 614a4b8df54a6f9ff3047b1bec5bce8cc9bbcf23 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Thu, 20 Dec 2012 19:05:26 -0800 Subject: [PATCH 060/217] Bug 806024 - Enable Android product announcements client service. r=trivial --- .../background/announcements/AnnouncementsConstants.java.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/background/announcements/AnnouncementsConstants.java.in b/mobile/android/base/background/announcements/AnnouncementsConstants.java.in index 0aef99e93bb5..1c88e8816687 100644 --- a/mobile/android/base/background/announcements/AnnouncementsConstants.java.in +++ b/mobile/android/base/background/announcements/AnnouncementsConstants.java.in @@ -11,7 +11,7 @@ import android.app.AlarmManager; public class AnnouncementsConstants { // Not `final` so we have the option to turn this on at runtime with a magic addon. - public static boolean DISABLED = true; + public static boolean DISABLED = false; public static final String GLOBAL_LOG_TAG = "GeckoAnnounce"; public static final String ACTION_ANNOUNCEMENTS_PREF = "@ANDROID_PACKAGE_NAME@.ANNOUNCEMENTS_PREF"; From 02e29ffc5f6b2dfc70988aead4c6f11bfb27c77c Mon Sep 17 00:00:00 2001 From: Marco Chen Date: Fri, 14 Dec 2012 13:02:54 +0800 Subject: [PATCH 061/217] Bug 819849 - One condition of judging playing state for AudioChannelAgent should be larger or equal to CURRENT not HAVE_FUTURE_DATA. r=roc, a=blocking-basecamp --- content/html/content/src/nsHTMLMediaElement.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) mode change 100644 => 100755 content/html/content/src/nsHTMLMediaElement.cpp diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp old mode 100644 new mode 100755 index 2225f679bbb3..ad6f19415526 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -3564,8 +3564,9 @@ void nsHTMLMediaElement::UpdateAudioChannelPlayingState() // The nsHTMLMediaElement is registered to the AudioChannelService only on B2G. #ifdef MOZ_B2G bool playingThroughTheAudioChannel = - (mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA && - IsPotentiallyPlaying()); + (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && + !mPaused && + !IsPlaybackEnded()); if (playingThroughTheAudioChannel != mPlayingThroughTheAudioChannel) { mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel; From 96d0c35bc4ed051fd46eb521f9121663977bcbf4 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Fri, 21 Dec 2012 12:16:44 +0900 Subject: [PATCH 062/217] Bug 818407, part1 - don't mix up with accessible types of ARIA role, r=tbsaunde --- accessible/src/atk/nsMaiInterfaceDocument.cpp | 1 + .../src/atk/nsMaiInterfaceEditableText.cpp | 1 + .../src/atk/nsMaiInterfaceHypertext.cpp | 1 + .../src/atk/nsMaiInterfaceSelection.cpp | 1 + accessible/src/atk/nsMaiInterfaceText.cpp | 1 + accessible/src/base/AccIterator.cpp | 2 +- accessible/src/base/nsARIAMap.h | 7 +++++ accessible/src/generic/Accessible-inl.h | 9 +++--- accessible/src/generic/Accessible.h | 30 +++++++++++-------- accessible/src/generic/OuterDocAccessible.cpp | 1 + accessible/src/mac/mozHTMLAccessible.mm | 1 + accessible/src/mac/mozTextAccessible.mm | 2 +- accessible/src/xul/XULAlertAccessible.cpp | 1 + accessible/src/xul/XULTreeAccessible.cpp | 1 + 14 files changed, 40 insertions(+), 19 deletions(-) diff --git a/accessible/src/atk/nsMaiInterfaceDocument.cpp b/accessible/src/atk/nsMaiInterfaceDocument.cpp index 9a3148695a00..b325941e1190 100644 --- a/accessible/src/atk/nsMaiInterfaceDocument.cpp +++ b/accessible/src/atk/nsMaiInterfaceDocument.cpp @@ -6,6 +6,7 @@ #include "InterfaceInitFuncs.h" +#include "Accessible-inl.h" #include "AccessibleWrap.h" #include "DocAccessible.h" #include "nsMai.h" diff --git a/accessible/src/atk/nsMaiInterfaceEditableText.cpp b/accessible/src/atk/nsMaiInterfaceEditableText.cpp index 663325ce86eb..e96d9324e3a2 100644 --- a/accessible/src/atk/nsMaiInterfaceEditableText.cpp +++ b/accessible/src/atk/nsMaiInterfaceEditableText.cpp @@ -6,6 +6,7 @@ #include "InterfaceInitFuncs.h" +#include "Accessible-inl.h" #include "HyperTextAccessible.h" #include "nsMai.h" diff --git a/accessible/src/atk/nsMaiInterfaceHypertext.cpp b/accessible/src/atk/nsMaiInterfaceHypertext.cpp index 08396623a61f..d0d836306060 100644 --- a/accessible/src/atk/nsMaiInterfaceHypertext.cpp +++ b/accessible/src/atk/nsMaiInterfaceHypertext.cpp @@ -6,6 +6,7 @@ #include "InterfaceInitFuncs.h" +#include "Accessible-inl.h" #include "HyperTextAccessible.h" #include "nsMai.h" #include "nsMaiHyperlink.h" diff --git a/accessible/src/atk/nsMaiInterfaceSelection.cpp b/accessible/src/atk/nsMaiInterfaceSelection.cpp index 350801c1a858..ca27126a766b 100644 --- a/accessible/src/atk/nsMaiInterfaceSelection.cpp +++ b/accessible/src/atk/nsMaiInterfaceSelection.cpp @@ -6,6 +6,7 @@ #include "InterfaceInitFuncs.h" +#include "Accessible-inl.h" #include "AccessibleWrap.h" #include "nsMai.h" #include "mozilla/Likely.h" diff --git a/accessible/src/atk/nsMaiInterfaceText.cpp b/accessible/src/atk/nsMaiInterfaceText.cpp index fcbb6556c5ee..52dc485bcc20 100644 --- a/accessible/src/atk/nsMaiInterfaceText.cpp +++ b/accessible/src/atk/nsMaiInterfaceText.cpp @@ -6,6 +6,7 @@ #include "InterfaceInitFuncs.h" +#include "Accessible-inl.h" #include "HyperTextAccessible.h" #include "nsMai.h" diff --git a/accessible/src/base/AccIterator.cpp b/accessible/src/base/AccIterator.cpp index 97daed43145e..f549c1491668 100644 --- a/accessible/src/base/AccIterator.cpp +++ b/accessible/src/base/AccIterator.cpp @@ -5,7 +5,7 @@ #include "AccIterator.h" #include "nsAccessibilityService.h" -#include "Accessible.h" +#include "Accessible-inl.h" #include "mozilla/dom/Element.h" #include "nsBindingManager.h" diff --git a/accessible/src/base/nsARIAMap.h b/accessible/src/base/nsARIAMap.h index 8f9e7048bcc0..7c9710eddbe2 100644 --- a/accessible/src/base/nsARIAMap.h +++ b/accessible/src/base/nsARIAMap.h @@ -9,6 +9,7 @@ #define _nsARIAMap_H_ #include "ARIAStateMap.h" +#include "mozilla/a11y/AccTypes.h" #include "mozilla/a11y/Role.h" #include "nsIAtom.h" @@ -149,6 +150,12 @@ struct nsRoleMapEntry bool Is(nsIAtom* aARIARole) const { return *roleAtom == aARIARole; } + /** + * Return true if ARIA role has the given accessible type. + */ + bool IsOfType(mozilla::a11y::AccGenericType aType) const + { return accTypes & aType; } + /** * Return ARIA role. */ diff --git a/accessible/src/generic/Accessible-inl.h b/accessible/src/generic/Accessible-inl.h index 0d463feb6d1b..3ee8a84902e9 100644 --- a/accessible/src/generic/Accessible-inl.h +++ b/accessible/src/generic/Accessible-inl.h @@ -31,12 +31,11 @@ Accessible::ARIARole() return ARIATransformRole(mRoleMapEntry->role); } -inline void -Accessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry) +inline bool +Accessible::HasGenericType(AccGenericType aType) const { - mRoleMapEntry = aRoleMapEntry; - if (mRoleMapEntry) - mGenericTypes |= mRoleMapEntry->accTypes; + return (mGenericTypes & aType) || + (mRoleMapEntry && mRoleMapEntry->IsOfType(aType)); } inline bool diff --git a/accessible/src/generic/Accessible.h b/accessible/src/generic/Accessible.h index 3e94e64a1b5a..9eb2e62ab4f8 100644 --- a/accessible/src/generic/Accessible.h +++ b/accessible/src/generic/Accessible.h @@ -304,7 +304,8 @@ public: /** * Set the ARIA role map entry for a new accessible. */ - void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry); + void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry) + { mRoleMapEntry = aRoleMapEntry; } /** * Update the children cache. @@ -468,17 +469,17 @@ public: bool IsApplication() const { return mType == eApplicationType; } - bool IsAutoComplete() const { return mGenericTypes & eAutoComplete; } + bool IsAutoComplete() const { return HasGenericType(eAutoComplete); } bool IsAutoCompletePopup() const - { return mGenericTypes & eAutoCompletePopup; } + { return HasGenericType(eAutoCompletePopup); } - bool IsCombobox() const { return mGenericTypes & eCombobox; } + bool IsCombobox() const { return HasGenericType(eCombobox); } - bool IsDoc() const { return mGenericTypes & eDocument; } + bool IsDoc() const { return HasGenericType(eDocument); } DocAccessible* AsDoc(); - bool IsHyperText() const { return mGenericTypes & eHyperText; } + bool IsHyperText() const { return HasGenericType(eHyperText); } HyperTextAccessible* AsHyperText(); bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; } @@ -494,11 +495,11 @@ public: bool IsImageMap() const { return mType == eImageMapType; } HTMLImageMapAccessible* AsImageMap(); - bool IsList() const { return mGenericTypes & eList; } + bool IsList() const { return HasGenericType(eList); } - bool IsListControl() const { return mGenericTypes & eListControl; } + bool IsListControl() const { return HasGenericType(eListControl); } - bool IsMenuButton() const { return mGenericTypes & eMenuButton; } + bool IsMenuButton() const { return HasGenericType(eMenuButton); } bool IsMenuPopup() const { return mType == eMenuPopupType; } @@ -507,14 +508,14 @@ public: bool IsRoot() const { return mType == eRootType; } a11y::RootAccessible* AsRoot(); - bool IsSelect() const { return mGenericTypes & eSelect; } + bool IsSelect() const { return HasGenericType(eSelect); } - bool IsTable() const { return mGenericTypes & eTable; } + bool IsTable() const { return HasGenericType(eTable); } virtual TableAccessible* AsTable() { return nullptr; } virtual TableCellAccessible* AsTableCell() { return nullptr; } - bool IsTableRow() const { return mGenericTypes & eTableRow; } + bool IsTableRow() const { return HasGenericType(eTableRow); } bool IsTextLeaf() const { return mType == eTextLeafType; } TextLeafAccessible* AsTextLeaf(); @@ -524,6 +525,11 @@ public: bool IsXULTree() const { return mType == eXULTreeType; } XULTreeAccessible* AsXULTree(); + /** + * Return true if the accessible belongs to the given accessible type. + */ + bool HasGenericType(AccGenericType aType) const; + ////////////////////////////////////////////////////////////////////////////// // ActionAccessible diff --git a/accessible/src/generic/OuterDocAccessible.cpp b/accessible/src/generic/OuterDocAccessible.cpp index d10496f0ec49..6ad340ffce50 100644 --- a/accessible/src/generic/OuterDocAccessible.cpp +++ b/accessible/src/generic/OuterDocAccessible.cpp @@ -5,6 +5,7 @@ #include "OuterDocAccessible.h" +#include "Accessible-inl.h" #include "nsAccUtils.h" #include "DocAccessible.h" #include "Role.h" diff --git a/accessible/src/mac/mozHTMLAccessible.mm b/accessible/src/mac/mozHTMLAccessible.mm index f9b88fbcf36f..c17231f45c5a 100644 --- a/accessible/src/mac/mozHTMLAccessible.mm +++ b/accessible/src/mac/mozHTMLAccessible.mm @@ -7,6 +7,7 @@ #import "mozHTMLAccessible.h" +#import "Accessible-inl.h" #import "HyperTextAccessible.h" #import "nsCocoaUtils.h" diff --git a/accessible/src/mac/mozTextAccessible.mm b/accessible/src/mac/mozTextAccessible.mm index 86da8028543d..f88c3490878a 100644 --- a/accessible/src/mac/mozTextAccessible.mm +++ b/accessible/src/mac/mozTextAccessible.mm @@ -3,7 +3,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/. */ - +#include "Accessible-inl.h" #include "AccessibleWrap.h" #include "TextLeafAccessible.h" diff --git a/accessible/src/xul/XULAlertAccessible.cpp b/accessible/src/xul/XULAlertAccessible.cpp index 010c5c3e7127..bc555b3a11cb 100644 --- a/accessible/src/xul/XULAlertAccessible.cpp +++ b/accessible/src/xul/XULAlertAccessible.cpp @@ -5,6 +5,7 @@ #include "XULAlertAccessible.h" +#include "Accessible-inl.h" #include "Role.h" #include "States.h" diff --git a/accessible/src/xul/XULTreeAccessible.cpp b/accessible/src/xul/XULTreeAccessible.cpp index 10ab9b2597f9..61535a7acbd0 100644 --- a/accessible/src/xul/XULTreeAccessible.cpp +++ b/accessible/src/xul/XULTreeAccessible.cpp @@ -6,6 +6,7 @@ #include "XULTreeAccessible.h" +#include "Accessible-inl.h" #include "DocAccessible-inl.h" #include "nsAccCache.h" #include "nsAccUtils.h" From 9866453c7a37b79f1aca20c23e1d2e73bd60a00e Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Thu, 20 Dec 2012 14:04:14 -0800 Subject: [PATCH 063/217] Bug 823705: Update NSS in Gecko to NSS 3.14.2 beta 1 (NSS_3_14_2_BETA1), r=me, a=wtc --HG-- extra : rebase_source : ae5ec41dab45b5a2c84a8f29acb3c3d6c85aa1f6 --- configure.in | 2 +- security/coreconf/SunOS5.10.mk | 2 - security/coreconf/SunOS5.10_i86pc.mk | 2 - security/coreconf/SunOS5.11.mk | 2 - security/coreconf/SunOS5.11_i86pc.mk | 2 - security/coreconf/SunOS5.3.mk | 8 - security/coreconf/SunOS5.4.mk | 8 - security/coreconf/SunOS5.4_i86pc.mk | 37 - security/coreconf/SunOS5.5.1.mk | 14 - security/coreconf/SunOS5.5.1_i86pc.mk | 16 - security/coreconf/SunOS5.5.mk | 12 - security/coreconf/SunOS5.6.mk | 14 - security/coreconf/SunOS5.6_i86pc.mk | 16 - security/coreconf/SunOS5.7.mk | 14 - security/coreconf/SunOS5.7_i86pc.mk | 18 - security/coreconf/SunOS5.8.mk | 2 - security/coreconf/SunOS5.8_i86pc.mk | 2 - security/coreconf/SunOS5.9.mk | 2 - security/coreconf/SunOS5.9_i86pc.mk | 2 - security/coreconf/coreconf.dep | 1 + security/nss/TAG-INFO | 2 +- security/nss/TAG-INFO-CKBI | 2 +- security/nss/cmd/certcgi/ca_form.html | 1 + security/nss/cmd/certcgi/certcgi.c | 5 + security/nss/cmd/certcgi/stnd_ext_form.html | 1 + security/nss/cmd/certutil/certext.c | 5 + security/nss/cmd/certutil/certutil.c | 291 +- security/nss/cmd/lib/basicutil.c | 2 +- security/nss/cmd/lib/moreoids.c | 11 + security/nss/cmd/lib/secutil.c | 4 +- security/nss/cmd/lib/secutil.h | 5 +- security/nss/cmd/lowhashtest/lowhashtest.c | 2 - security/nss/cmd/pwdecrypt/pwdecrypt.c | 3 +- security/nss/lib/certhigh/ocsp.c | 14 +- security/nss/lib/certhigh/ocsp.h | 2 +- security/nss/lib/certhigh/ocspi.h | 4 +- security/nss/lib/certhigh/ocspt.h | 2 +- security/nss/lib/certhigh/ocspti.h | 2 +- security/nss/lib/freebl/drbg.c | 4 +- security/nss/lib/freebl/loader.c | 6 +- security/nss/lib/freebl/pqg.c | 4 +- security/nss/lib/freebl/unix_rand.c | 9 - security/nss/lib/nss/nss.h | 8 +- security/nss/lib/pkcs7/p7decode.c | 3 +- security/nss/lib/pki/pki3hack.c | 4 +- security/nss/lib/smime/cmsasn1.c | 6 +- security/nss/lib/softoken/legacydb/pcertdb.c | 4 +- security/nss/lib/softoken/pkcs11c.c | 38 +- security/nss/lib/softoken/sdb.c | 147 +- security/nss/lib/softoken/softkver.h | 6 +- security/nss/lib/sqlite/README | 5 +- security/nss/lib/sqlite/config.mk | 10 + security/nss/lib/sqlite/sqlite.def | 3 + security/nss/lib/sqlite/sqlite3.c | 63845 ++++++++++++----- security/nss/lib/sqlite/sqlite3.h | 2516 +- security/nss/lib/ssl/sslsock.c | 3 +- security/nss/lib/util/nssutil.h | 6 +- security/nss/lib/util/secoid.c | 15 +- security/nss/lib/util/secoidt.h | 8 +- security/nss/tests/lowhash/lowhash.sh | 11 +- security/nss/tests/ssl/ssl.sh | 11 +- 61 files changed, 47545 insertions(+), 19661 deletions(-) delete mode 100644 security/coreconf/SunOS5.3.mk delete mode 100644 security/coreconf/SunOS5.4.mk delete mode 100644 security/coreconf/SunOS5.4_i86pc.mk delete mode 100644 security/coreconf/SunOS5.5.1.mk delete mode 100644 security/coreconf/SunOS5.5.1_i86pc.mk delete mode 100644 security/coreconf/SunOS5.5.mk delete mode 100644 security/coreconf/SunOS5.6.mk delete mode 100644 security/coreconf/SunOS5.6_i86pc.mk delete mode 100644 security/coreconf/SunOS5.7.mk delete mode 100644 security/coreconf/SunOS5.7_i86pc.mk diff --git a/configure.in b/configure.in index 0b5251ec709b..4eebea830d1d 100644 --- a/configure.in +++ b/configure.in @@ -3995,7 +3995,7 @@ MOZ_ARG_WITH_BOOL(system-nss, _USE_SYSTEM_NSS=1 ) if test -n "$_USE_SYSTEM_NSS"; then - AM_PATH_NSS(3.14.1, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])]) + AM_PATH_NSS(3.14.2, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])]) fi if test -n "$MOZ_NATIVE_NSS"; then diff --git a/security/coreconf/SunOS5.10.mk b/security/coreconf/SunOS5.10.mk index fc0855fe75e1..171b97bde951 100644 --- a/security/coreconf/SunOS5.10.mk +++ b/security/coreconf/SunOS5.10.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS += -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk ifeq ($(OS_RELEASE),5.10) diff --git a/security/coreconf/SunOS5.10_i86pc.mk b/security/coreconf/SunOS5.10_i86pc.mk index 7a38133ff4f9..f392b06773c2 100755 --- a/security/coreconf/SunOS5.10_i86pc.mk +++ b/security/coreconf/SunOS5.10_i86pc.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS = -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk ifeq ($(USE_64),1) diff --git a/security/coreconf/SunOS5.11.mk b/security/coreconf/SunOS5.11.mk index 8ffbc3f196ae..d1686d523dce 100644 --- a/security/coreconf/SunOS5.11.mk +++ b/security/coreconf/SunOS5.11.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS += -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk ifeq ($(OS_RELEASE),5.11) diff --git a/security/coreconf/SunOS5.11_i86pc.mk b/security/coreconf/SunOS5.11_i86pc.mk index 5010f3a21abf..72f586cf6f28 100644 --- a/security/coreconf/SunOS5.11_i86pc.mk +++ b/security/coreconf/SunOS5.11_i86pc.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS = -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk ifeq ($(USE_64),1) diff --git a/security/coreconf/SunOS5.3.mk b/security/coreconf/SunOS5.3.mk deleted file mode 100644 index 4ba3f522df42..000000000000 --- a/security/coreconf/SunOS5.3.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS = - -include $(CORE_DEPTH)/coreconf/SunOS5.mk diff --git a/security/coreconf/SunOS5.4.mk b/security/coreconf/SunOS5.4.mk deleted file mode 100644 index 4ba3f522df42..000000000000 --- a/security/coreconf/SunOS5.4.mk +++ /dev/null @@ -1,8 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS = - -include $(CORE_DEPTH)/coreconf/SunOS5.mk diff --git a/security/coreconf/SunOS5.4_i86pc.mk b/security/coreconf/SunOS5.4_i86pc.mk deleted file mode 100644 index e8a603aedf10..000000000000 --- a/security/coreconf/SunOS5.4_i86pc.mk +++ /dev/null @@ -1,37 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -include $(CORE_DEPTH)/coreconf/UNIX.mk - -DEFAULT_COMPILER = cc - -ifdef NS_USE_GCC - CC = gcc - OS_CFLAGS += -Wall -Wno-format -Wno-switch - CCC = g++ - CCC += -Wall -Wno-format - ASFLAGS += -x assembler-with-cpp - OS_CFLAGS += $(NOMD_OS_CFLAGS) - ifdef USE_MDUPDATE - OS_CFLAGS += -MDupdate $(DEPENDENCIES) - endif -else - CC = cc - CCC = CC - ASFLAGS += -Wa,-P - OS_CFLAGS += $(NOMD_OS_CFLAGS) -endif - -CPU_ARCH = x86 - -MKSHLIB = $(LD) -MKSHLIB += $(DSO_LDOPTS) -NOSUCHFILE = /solx86-rm-f-sucks -RANLIB = echo - -# for purify -NOMD_OS_CFLAGS += -DSVR4 -DSYSV -D_REENTRANT -DSOLARIS -D__svr4__ -Di386 - -DSO_LDOPTS += -G diff --git a/security/coreconf/SunOS5.5.1.mk b/security/coreconf/SunOS5.5.1.mk deleted file mode 100644 index 814c70fb8e56..000000000000 --- a/security/coreconf/SunOS5.5.1.mk +++ /dev/null @@ -1,14 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS += -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -ifeq ($(OS_RELEASE),5.5.1) - OS_DEFINES += -DSOLARIS2_5 -endif - -OS_LIBS += -lthread -lnsl -lsocket -lposix4 -ldl -lc diff --git a/security/coreconf/SunOS5.5.1_i86pc.mk b/security/coreconf/SunOS5.5.1_i86pc.mk deleted file mode 100644 index ddc7d267aac5..000000000000 --- a/security/coreconf/SunOS5.5.1_i86pc.mk +++ /dev/null @@ -1,16 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS = -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -CPU_ARCH = x86 -ARCHFLAG = -OS_DEFINES += -Di386 - -ifeq ($(OS_RELEASE),5.5.1_i86pc) - OS_DEFINES += -DSOLARIS2_5 -endif diff --git a/security/coreconf/SunOS5.5.mk b/security/coreconf/SunOS5.5.mk deleted file mode 100644 index d9e205680f9c..000000000000 --- a/security/coreconf/SunOS5.5.mk +++ /dev/null @@ -1,12 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS += -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -ifeq ($(OS_RELEASE),5.5) - OS_DEFINES += -DSOLARIS2_5 -endif diff --git a/security/coreconf/SunOS5.6.mk b/security/coreconf/SunOS5.6.mk deleted file mode 100644 index ed609f92442a..000000000000 --- a/security/coreconf/SunOS5.6.mk +++ /dev/null @@ -1,14 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS += -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -ifeq ($(OS_RELEASE),5.6) - OS_DEFINES += -DSOLARIS2_6 -endif - -OS_LIBS += -lthread -lnsl -lsocket -lposix4 -ldl -lc diff --git a/security/coreconf/SunOS5.6_i86pc.mk b/security/coreconf/SunOS5.6_i86pc.mk deleted file mode 100644 index faef79d4629f..000000000000 --- a/security/coreconf/SunOS5.6_i86pc.mk +++ /dev/null @@ -1,16 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS = -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -CPU_ARCH = x86 -ARCHFLAG = -OS_DEFINES += -Di386 - -ifeq ($(OS_RELEASE),5.6_i86pc) - OS_DEFINES += -DSOLARIS2_6 -endif diff --git a/security/coreconf/SunOS5.7.mk b/security/coreconf/SunOS5.7.mk deleted file mode 100644 index 3c70149b992c..000000000000 --- a/security/coreconf/SunOS5.7.mk +++ /dev/null @@ -1,14 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS += -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -ifeq ($(OS_RELEASE),5.7) - OS_DEFINES += -DSOLARIS2_7 -endif - -OS_LIBS += -lthread -lnsl -lsocket -lposix4 -ldl -lc diff --git a/security/coreconf/SunOS5.7_i86pc.mk b/security/coreconf/SunOS5.7_i86pc.mk deleted file mode 100644 index f27839ecd5aa..000000000000 --- a/security/coreconf/SunOS5.7_i86pc.mk +++ /dev/null @@ -1,18 +0,0 @@ -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -SOL_CFLAGS = -D_SVID_GETTOD - -include $(CORE_DEPTH)/coreconf/SunOS5.mk - -CPU_ARCH = x86 -ARCHFLAG = -OS_DEFINES += -Di386 - -ifeq ($(OS_RELEASE),5.7_i86pc) - OS_DEFINES += -DSOLARIS2_7 -endif - -OS_LIBS += -lthread -lnsl -lsocket -lposix4 -ldl -lc diff --git a/security/coreconf/SunOS5.8.mk b/security/coreconf/SunOS5.8.mk index dec1f7233afd..9e5c56188af2 100644 --- a/security/coreconf/SunOS5.8.mk +++ b/security/coreconf/SunOS5.8.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS += -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk ifeq ($(OS_RELEASE),5.8) diff --git a/security/coreconf/SunOS5.8_i86pc.mk b/security/coreconf/SunOS5.8_i86pc.mk index e403b0b938fe..8933cb174898 100644 --- a/security/coreconf/SunOS5.8_i86pc.mk +++ b/security/coreconf/SunOS5.8_i86pc.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS = -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk CPU_ARCH = x86 diff --git a/security/coreconf/SunOS5.9.mk b/security/coreconf/SunOS5.9.mk index 02396d7871aa..bbb235e00fe8 100755 --- a/security/coreconf/SunOS5.9.mk +++ b/security/coreconf/SunOS5.9.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS += -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk ifeq ($(OS_RELEASE),5.9) diff --git a/security/coreconf/SunOS5.9_i86pc.mk b/security/coreconf/SunOS5.9_i86pc.mk index c1b209a6d28d..41b4601d93fd 100755 --- a/security/coreconf/SunOS5.9_i86pc.mk +++ b/security/coreconf/SunOS5.9_i86pc.mk @@ -3,8 +3,6 @@ # 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/. -SOL_CFLAGS = -D_SVID_GETTOD - include $(CORE_DEPTH)/coreconf/SunOS5.mk CPU_ARCH = x86 diff --git a/security/coreconf/coreconf.dep b/security/coreconf/coreconf.dep index 5182f75552c8..590d1bfaeee3 100644 --- a/security/coreconf/coreconf.dep +++ b/security/coreconf/coreconf.dep @@ -10,3 +10,4 @@ */ #error "Do not include this header file." + diff --git a/security/nss/TAG-INFO b/security/nss/TAG-INFO index 5881889359f4..73bc101b1210 100644 --- a/security/nss/TAG-INFO +++ b/security/nss/TAG-INFO @@ -1 +1 @@ -NSS_3_14_1_RC0 +NSS_3_14_2_BETA1 diff --git a/security/nss/TAG-INFO-CKBI b/security/nss/TAG-INFO-CKBI index 5881889359f4..73bc101b1210 100644 --- a/security/nss/TAG-INFO-CKBI +++ b/security/nss/TAG-INFO-CKBI @@ -1 +1 @@ -NSS_3_14_1_RC0 +NSS_3_14_2_BETA1 diff --git a/security/nss/cmd/certcgi/ca_form.html b/security/nss/cmd/certcgi/ca_form.html index 02a242e1a22f..452996b58d1b 100644 --- a/security/nss/cmd/certcgi/ca_form.html +++ b/security/nss/cmd/certcgi/ca_form.html @@ -167,6 +167,7 @@ Timestamp

OCSP Responder

Step-up

+ Microsoft Trust List Signing

diff --git a/security/nss/cmd/certcgi/certcgi.c b/security/nss/cmd/certcgi/certcgi.c index a595cb71e271..c0142d57d2fe 100644 --- a/security/nss/cmd/certcgi/certcgi.c +++ b/security/nss/cmd/certcgi/certcgi.c @@ -819,6 +819,11 @@ AddExtKeyUsage(void *extHandle, Pair *data) if( SECSuccess != rv ) goto loser; } + if( find_field_bool(data, "extKeyUsage-msTrustListSign", PR_TRUE) ) { + rv = AddOidToSequence(os, SEC_OID_MS_EXT_KEY_USAGE_CTL_SIGNING); + if( SECSuccess != rv ) goto loser; + } + if( find_field_bool(data, "extKeyUsage-clientAuth", PR_TRUE) ) { rv = AddOidToSequence(os, SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH); if( SECSuccess != rv ) goto loser; diff --git a/security/nss/cmd/certcgi/stnd_ext_form.html b/security/nss/cmd/certcgi/stnd_ext_form.html index 0df580e80169..60d4d86a0b74 100644 --- a/security/nss/cmd/certcgi/stnd_ext_form.html +++ b/security/nss/cmd/certcgi/stnd_ext_form.html @@ -34,6 +34,7 @@ Timestamp

OCSP Responder

Step-up

+ Microsoft Trust List Signing

diff --git a/security/nss/cmd/certutil/certext.c b/security/nss/cmd/certutil/certext.c index 9d9b87314ea6..a930468f1a87 100644 --- a/security/nss/cmd/certutil/certext.c +++ b/security/nss/cmd/certutil/certext.c @@ -483,6 +483,7 @@ extKeyUsageKeyWordArray[] = { "serverAuth", "timeStamp", "ocspResponder", "stepUp", + "msTrustListSigning", NULL}; static SECStatus @@ -511,6 +512,7 @@ AddExtKeyUsage (void *extHandle, const char *userSuppliedValue) "\t\t4 - Timestamp\n" "\t\t5 - OCSP Responder\n" "\t\t6 - Step-up\n" + "\t\t7 - Microsoft Trust List Signing\n" "\t\tOther to finish\n", buffer, sizeof(buffer)) == SECFailure) { GEN_BREAK(SECFailure); @@ -554,6 +556,9 @@ AddExtKeyUsage (void *extHandle, const char *userSuppliedValue) case 6: rv = AddOidToSequence(os, SEC_OID_NS_KEY_USAGE_GOVT_APPROVED); break; + case 7: + rv = AddOidToSequence(os, SEC_OID_MS_EXT_KEY_USAGE_CTL_SIGNING); + break; default: goto endloop; } diff --git a/security/nss/cmd/certutil/certutil.c b/security/nss/cmd/certutil/certutil.c index c78d8e568b58..acec6d4b51b5 100644 --- a/security/nss/cmd/certutil/certutil.c +++ b/security/nss/cmd/certutil/certutil.c @@ -47,25 +47,19 @@ char *progName; static CERTCertificateRequest * -GetCertRequest(PRFileDesc *inFile, PRBool ascii) +GetCertRequest(const SECItem *reqDER) { CERTCertificateRequest *certReq = NULL; CERTSignedData signedData; PRArenaPool *arena = NULL; - SECItem reqDER; SECStatus rv; - reqDER.data = NULL; do { arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { GEN_BREAK (SECFailure); } - rv = SECU_ReadDERFromFile(&reqDER, inFile, ascii); - if (rv) { - break; - } certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc (arena, sizeof(CERTCertificateRequest)); if (!certReq) { @@ -78,7 +72,7 @@ GetCertRequest(PRFileDesc *inFile, PRBool ascii) */ PORT_Memset(&signedData, 0, sizeof(signedData)); rv = SEC_ASN1DecodeItem(arena, &signedData, - SEC_ASN1_GET(CERT_SignedDataTemplate), &reqDER); + SEC_ASN1_GET(CERT_SignedDataTemplate), reqDER); if (rv) { break; } @@ -91,10 +85,6 @@ GetCertRequest(PRFileDesc *inFile, PRBool ascii) &certReq->subjectPublicKeyInfo, NULL /* wincx */); } while (0); - if (reqDER.data) { - SECITEM_FreeItem(&reqDER, PR_FALSE); - } - if (rv) { SECU_PrintError(progName, "bad certificate request\n"); if (arena) { @@ -108,26 +98,17 @@ GetCertRequest(PRFileDesc *inFile, PRBool ascii) static SECStatus AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, char *name, char *trusts, - PRFileDesc *inFile, PRBool ascii, PRBool emailcert, void *pwdata) + const SECItem *certDER, PRBool emailcert, void *pwdata) { CERTCertTrust *trust = NULL; CERTCertificate *cert = NULL; - SECItem certDER; SECStatus rv; - certDER.data = NULL; do { - /* Read in the entire file specified with the -i argument */ - rv = SECU_ReadDERFromFile(&certDER, inFile, ascii); - if (rv != SECSuccess) { - SECU_PrintError(progName, "unable to read input file"); - break; - } - /* Read in an ASCII cert and return a CERTCertificate */ - cert = CERT_DecodeCertFromPackage((char *)certDER.data, certDER.len); + cert = CERT_DecodeCertFromPackage((char *)certDER->data, certDER->len); if (!cert) { - SECU_PrintError(progName, "could not obtain certificate from file"); + SECU_PrintError(progName, "could not decode certificate"); GEN_BREAK(SECFailure); } @@ -193,7 +174,6 @@ AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, char *name, char *trusts, CERT_DestroyCertificate (cert); PORT_Free(trust); - PORT_Free(certDER.data); return rv; } @@ -203,17 +183,16 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, SECOidTag hashAlgTag, CERTName *subject, char *phone, int ascii, const char *emailAddrs, const char *dnsNames, certutilExtnList extnList, - PRFileDesc *outFile) + /*out*/ SECItem *result) { CERTSubjectPublicKeyInfo *spki; CERTCertificateRequest *cr; SECItem *encoding; SECOidTag signAlgTag; - SECItem result; SECStatus rv; PRArenaPool *arena; - PRInt32 numBytes; void *extHandle; + SECItem signedReq = { siBuffer, NULL, 0 }; /* Create info about public key */ spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); @@ -266,8 +245,9 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, SECU_PrintError(progName, "unknown Key or Hash type"); return SECFailure; } - rv = SEC_DerSignData(arena, &result, encoding->data, encoding->len, - privk, signAlgTag); + + rv = SEC_DerSignData(arena, &signedReq, encoding->data, encoding->len, + privk, signAlgTag); if (rv) { PORT_FreeArena (arena, PR_FALSE); SECU_PrintError(progName, "signing of data failed"); @@ -277,14 +257,12 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, /* Encode request in specified format */ if (ascii) { char *obuf; - char *name, *email, *org, *state, *country; - SECItem *it; - int total; + char *header, *name, *email, *org, *state, *country; - it = &result; - - obuf = BTOA_ConvertItemToAscii(it); - total = PL_strlen(obuf); + obuf = BTOA_ConvertItemToAscii(&signedReq); + if (!obuf) { + goto oom; + } name = CERT_GetCommonName(subject); if (!name) { @@ -310,14 +288,16 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, if (!country) country = PORT_Strdup("(not specified)"); - PR_fprintf(outFile, - "\nCertificate request generated by Netscape certutil\n"); - PR_fprintf(outFile, "Phone: %s\n\n", phone); - PR_fprintf(outFile, "Common Name: %s\n", name); - PR_fprintf(outFile, "Email: %s\n", email); - PR_fprintf(outFile, "Organization: %s\n", org); - PR_fprintf(outFile, "State: %s\n", state); - PR_fprintf(outFile, "Country: %s\n\n", country); + header = PR_smprintf( + "\nCertificate request generated by Netscape certutil\n" + "Phone: %s\n\n" + "Common Name: %s\n" + "Email: %s\n" + "Organization: %s\n" + "State: %s\n" + "Country: %s\n\n" + "%s\n", + phone, name, email, org, state, country, NS_CERTREQ_HEADER); PORT_Free(name); PORT_Free(email); @@ -325,25 +305,36 @@ CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, PORT_Free(state); PORT_Free(country); - PR_fprintf(outFile, "%s\n", NS_CERTREQ_HEADER); - numBytes = PR_Write(outFile, obuf, total); - PORT_Free(obuf); - if (numBytes != total) { - PORT_FreeArena (arena, PR_FALSE); - SECU_PrintError(progName, "write error"); - return SECFailure; + if (header) { + char * trailer = PR_smprintf("\n%s\n", NS_CERTREQ_TRAILER); + if (trailer) { + PRUint32 headerLen = PL_strlen(header); + PRUint32 obufLen = PL_strlen(obuf); + PRUint32 trailerLen = PL_strlen(trailer); + SECITEM_AllocItem(NULL, result, + headerLen + obufLen + trailerLen); + if (!result->data) { + PORT_Memcpy(result->data, header, headerLen); + PORT_Memcpy(result->data + headerLen, obuf, obufLen); + PORT_Memcpy(result->data + headerLen + obufLen, + trailer, trailerLen); + } + PR_smprintf_free(trailer); + } + PR_smprintf_free(header); } - PR_fprintf(outFile, "\n%s\n", NS_CERTREQ_TRAILER); } else { - numBytes = PR_Write(outFile, result.data, result.len); - if (numBytes != (int)result.len) { - PORT_FreeArena (arena, PR_FALSE); - SECU_PrintSystemError(progName, "write error"); - return SECFailure; - } + (void) SECITEM_CopyItem(NULL, result, &signedReq); } + + if (!result->data) { +oom: SECU_PrintError(progName, "out of memory"); + PORT_SetError(SEC_ERROR_NO_MEMORY); + rv = SECFailure; + } + PORT_FreeArena (arena, PR_FALSE); - return SECSuccess; + return rv; } static SECStatus @@ -1143,7 +1134,7 @@ static void luC(enum usage_level ul, const char *command) "%-20s Create extended key usage extension. Possible keywords:\n" "%-20s \"serverAuth\", \"clientAuth\",\"codeSigning\",\n" "%-20s \"emailProtection\", \"timeStamp\",\"ocspResponder\",\n" - "%-20s \"stepUp\", \"critical\"\n", + "%-20s \"stepUp\", \"msTrustListSign\", \"critical\"\n", " -6 | --extKeyUsage keyword,keyword,...", "", "", "", ""); FPS "%-20s Create an email subject alt name extension\n", " -7 emailAddrs"); @@ -1700,13 +1691,12 @@ MakeV1Cert( CERTCertDBHandle * handle, return(cert); } -static SECItem * +static SECStatus SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, SECOidTag hashAlgTag, SECKEYPrivateKey *privKey, char *issuerNickName, void *pwarg) { SECItem der; - SECItem *result = NULL; SECKEYPrivateKey *caPrivateKey = NULL; SECStatus rv; PRArenaPool *arena; @@ -1718,14 +1708,14 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, if( (CERTCertificate *)NULL == issuer ) { SECU_PrintError(progName, "unable to find issuer with nickname %s", issuerNickName); - return (SECItem *)NULL; + return SECFailure; } privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); CERT_DestroyCertificate(issuer); if (caPrivateKey == NULL) { SECU_PrintError(progName, "unable to retrieve key %s", issuerNickName); - return NULL; + return SECFailure; } } @@ -1734,6 +1724,7 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag); if (algID == SEC_OID_UNKNOWN) { fprintf(stderr, "Unknown key or hash type for issuer."); + rv = SECFailure; goto done; } @@ -1753,29 +1744,22 @@ SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, SEC_ASN1_GET(CERT_CertificateTemplate)); if (!dummy) { fprintf (stderr, "Could not encode certificate.\n"); + rv = SECFailure; goto done; } - result = (SECItem *) PORT_ArenaZAlloc (arena, sizeof (SECItem)); - if (result == NULL) { - fprintf (stderr, "Could not allocate item for certificate data.\n"); - goto done; - } - - rv = SEC_DerSignData(arena, result, der.data, der.len, privKey, algID); + rv = SEC_DerSignData(arena, &cert->derCert, der.data, der.len, privKey, algID); if (rv != SECSuccess) { fprintf (stderr, "Could not sign encoded certificate data.\n"); /* result allocated out of the arena, it will be freed * when the arena is freed */ - result = NULL; goto done; } - cert->derCert = *result; done: if (caPrivateKey) { SECKEY_DestroyPrivateKey(caPrivateKey); } - return result; + return rv; } static SECStatus @@ -1783,8 +1767,7 @@ CreateCert( CERTCertDBHandle *handle, PK11SlotInfo *slot, char * issuerNickName, - PRFileDesc *inFile, - PRFileDesc *outFile, + const SECItem * certReqDER, SECKEYPrivateKey **selfsignprivkey, void *pwarg, SECOidTag hashAlgTag, @@ -1793,22 +1776,20 @@ CreateCert( int validityMonths, const char *emailAddrs, const char *dnsNames, - PRBool ascii, + PRBool ascii, PRBool selfsign, - certutilExtnList extnList) + certutilExtnList extnList, + SECItem * certDER) { void * extHandle; - SECItem * certDER; CERTCertificate *subjectCert = NULL; CERTCertificateRequest *certReq = NULL; SECStatus rv = SECSuccess; - SECItem reqDER; CERTCertExtension **CRexts; - reqDER.data = NULL; do { /* Create a certrequest object from the input cert request der */ - certReq = GetCertRequest(inFile, ascii); + certReq = GetCertRequest(certReqDER); if (certReq == NULL) { GEN_BREAK (SECFailure) } @@ -1856,19 +1837,33 @@ CreateCert( } } - certDER = SignCert(handle, subjectCert, selfsign, hashAlgTag, - *selfsignprivkey, issuerNickName,pwarg); + rv = SignCert(handle, subjectCert, selfsign, hashAlgTag, + *selfsignprivkey, issuerNickName, pwarg); + if (rv != SECSuccess) + break; - if (certDER) { - if (ascii) { - PR_fprintf(outFile, "%s\n%s\n%s\n", NS_CERT_HEADER, - BTOA_DataToAscii(certDER->data, certDER->len), - NS_CERT_TRAILER); - } else { - PR_Write(outFile, certDER->data, certDER->len); - } + rv = SECFailure; + if (ascii) { + char * asciiDER = BTOA_DataToAscii(subjectCert->derCert.data, + subjectCert->derCert.len); + if (asciiDER) { + char * wrapped = PR_smprintf("%s\n%s\n%s\n", + NS_CERT_HEADER, + asciiDER, + NS_CERT_TRAILER); + if (wrapped) { + PRUint32 wrappedLen = PL_strlen(wrapped); + if (SECITEM_AllocItem(NULL, certDER, wrappedLen)) { + PORT_Memcpy(certDER->data, wrapped, wrappedLen); + rv = SECSuccess; + } + PR_smprintf_free(wrapped); + } + PORT_Free(asciiDER); + } + } else { + rv = SECITEM_CopyItem(NULL, certDER, &subjectCert->derCert); } - } while (0); CERT_DestroyCertificateRequest (certReq); CERT_DestroyCertificate (subjectCert); @@ -2179,9 +2174,9 @@ certutil_main(int argc, char **argv, PRBool initialize) PK11SlotInfo *slot = NULL; CERTName * subject = 0; PRFileDesc *inFile = PR_STDIN; - PRFileDesc *outFile = NULL; - char * certfile = "tempcert"; - char * certreqfile = "tempcertreq"; + PRFileDesc *outFile = PR_STDOUT; + SECItem certReqDER = { siBuffer, NULL, 0 }; + SECItem certDER = { siBuffer, NULL, 0 }; char * slotname = "internal"; char * certPrefix = ""; char * sourceDir = ""; @@ -2573,19 +2568,6 @@ certutil_main(int argc, char **argv, PRBool initialize) return 255; } - /* -S open outFile, temporary file for cert request. */ - if (certutil.commands[cmd_CreateAndAddCert].activated) { - outFile = PR_Open(certreqfile, - PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00660); - if (!outFile) { - PR_fprintf(PR_STDERR, - "%s -o: unable to open \"%s\" for writing (%ld, %ld)\n", - progName, certreqfile, - PR_GetError(), PR_GetOSError()); - return 255; - } - } - /* Open the input file. */ if (certutil.options[opt_InputFile].activated) { inFile = PR_Open(certutil.options[opt_InputFile].arg, PR_RDONLY, 0); @@ -2599,7 +2581,7 @@ certutil_main(int argc, char **argv, PRBool initialize) } /* Open the output file. */ - if (certutil.options[opt_OutputFile].activated && !outFile) { + if (certutil.options[opt_OutputFile].activated) { outFile = PR_Open(certutil.options[opt_OutputFile].arg, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); if (!outFile) { @@ -2649,9 +2631,6 @@ certutil_main(int argc, char **argv, PRBool initialize) else if (slotname != NULL) slot = PK11_FindSlotByName(slotname); - - - if ( !slot && (certutil.commands[cmd_NewDBs].activated || certutil.commands[cmd_ModifyCertTrust].activated || certutil.commands[cmd_ChangePassword].activated || @@ -2806,7 +2785,7 @@ merge_fail: rv = ListCerts(certHandle, name, email, slot, certutil.options[opt_BinaryDER].activated, certutil.options[opt_ASCIIForIO].activated, - (outFile) ? outFile : PR_STDOUT, &pwdata); + outFile, &pwdata); goto shutdown; } if (certutil.commands[cmd_DumpChain].activated) { @@ -3006,6 +2985,18 @@ merge_fail: certutil_extns[ext_inhibitAnyPolicy].activated = certutil.options[opt_AddInhibAnyExt].activated; } + + /* -A -C or -E Read inFile */ + if (certutil.commands[cmd_CreateNewCert].activated || + certutil.commands[cmd_AddCert].activated || + certutil.commands[cmd_AddEmailCert].activated) { + PRBool isCreate = certutil.commands[cmd_CreateNewCert].activated; + rv = SECU_ReadDERFromFile(isCreate ? &certReqDER : &certDER, inFile, + certutil.options[opt_ASCIIForIO].activated); + if (rv) + goto shutdown; + } + /* * Certificate request */ @@ -3018,8 +3009,8 @@ merge_fail: certutil.options[opt_ExtendedEmailAddrs].arg, certutil.options[opt_ExtendedDNSNames].arg, certutil_extns, - outFile ? outFile : PR_STDOUT); - if (rv) + &certReqDER); + if (rv) goto shutdown; privkey->wincx = &pwdata; } @@ -3036,31 +3027,14 @@ merge_fail: static certutilExtnList nullextnlist = {{PR_FALSE, NULL}}; rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, certutil.options[opt_PhoneNumber].arg, - certutil.options[opt_ASCIIForIO].activated, + PR_FALSE, /* do not BASE64-encode regardless of -a option */ NULL, NULL, nullextnlist, - outFile ? outFile : PR_STDOUT); + &certReqDER); if (rv) goto shutdown; privkey->wincx = &pwdata; - PR_Close(outFile); - outFile = NULL; - inFile = PR_Open(certreqfile, PR_RDONLY, 0); - if (!inFile) { - PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n", - certreqfile, PR_GetError(), PR_GetOSError()); - rv = SECFailure; - goto shutdown; - } - outFile = PR_Open(certfile, - PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00660); - if (!outFile) { - PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n", - certfile, PR_GetError(), PR_GetOSError()); - rv = SECFailure; - goto shutdown; - } } /* Create a certificate (-C or -S). */ @@ -3068,13 +3042,15 @@ merge_fail: certutil.commands[cmd_CreateNewCert].activated) { rv = CreateCert(certHandle, slot, certutil.options[opt_IssuerName].arg, - inFile, outFile, &privkey, &pwdata, hashAlgTag, + &certReqDER, &privkey, &pwdata, hashAlgTag, serialNumber, warpmonths, validityMonths, certutil.options[opt_ExtendedEmailAddrs].arg, certutil.options[opt_ExtendedDNSNames].arg, - certutil.options[opt_ASCIIForIO].activated, + certutil.options[opt_ASCIIForIO].activated && + certutil.commands[cmd_CreateNewCert].activated, certutil.options[opt_SelfSign].activated, - certutil_extns); + certutil_extns, + &certDER); if (rv) goto shutdown; } @@ -3082,20 +3058,6 @@ merge_fail: /* * Adding a cert to the database (or slot) */ - - if (certutil.commands[cmd_CreateAndAddCert].activated) { - PORT_Assert(inFile != PR_STDIN); - PR_Close(inFile); - PR_Close(outFile); - outFile = NULL; - inFile = PR_Open(certfile, PR_RDONLY, 0); - if (!inFile) { - PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n", - certfile, PR_GetError(), PR_GetOSError()); - rv = SECFailure; - goto shutdown; - } - } /* -A -E or -S Add the cert to the DB */ if (certutil.commands[cmd_CreateAndAddCert].activated || @@ -3103,18 +3065,20 @@ merge_fail: certutil.commands[cmd_AddEmailCert].activated) { rv = AddCert(slot, certHandle, name, certutil.options[opt_Trust].arg, - inFile, - certutil.options[opt_ASCIIForIO].activated, + &certDER, certutil.commands[cmd_AddEmailCert].activated,&pwdata); if (rv) goto shutdown; } - if (certutil.commands[cmd_CreateAndAddCert].activated) { - PORT_Assert(inFile != PR_STDIN); - PR_Close(inFile); - PR_Delete(certfile); - PR_Delete(certreqfile); + if (certutil.commands[cmd_CertReq].activated || + certutil.commands[cmd_CreateNewCert].activated) { + SECItem * item = certutil.commands[cmd_CertReq].activated ? &certReqDER + : &certDER; + PRInt32 written = PR_Write(outFile, item->data, item->len); + if (written < 0 || (PRUint32) written != item->len) { + rv = SECFailure; + } } shutdown: @@ -3133,9 +3097,14 @@ shutdown: if (name) { PL_strfree(name); } - if (outFile) { + if (inFile && inFile != PR_STDIN) { + PR_Close(inFile); + } + if (outFile && outFile != PR_STDOUT) { PR_Close(outFile); } + SECITEM_FreeItem(&certReqDER, PR_FALSE); + SECITEM_FreeItem(&certDER, PR_FALSE); if (pwdata.data && pwdata.source == PW_PLAINTEXT) { /* Allocated by a PL_strdup call in SECU_GetModulePassword. */ PL_strfree(pwdata.data); diff --git a/security/nss/cmd/lib/basicutil.c b/security/nss/cmd/lib/basicutil.c index a560ca875778..d2e36ed533db 100644 --- a/security/nss/cmd/lib/basicutil.c +++ b/security/nss/cmd/lib/basicutil.c @@ -37,7 +37,7 @@ SECU_EnableWrap(PRBool enable) } PRBool -SECU_GetWrapEnabled() +SECU_GetWrapEnabled(void) { return wrapEnabled; } diff --git a/security/nss/cmd/lib/moreoids.c b/security/nss/cmd/lib/moreoids.c index 652df283ba9c..6c184764c99b 100644 --- a/security/nss/cmd/lib/moreoids.c +++ b/security/nss/cmd/lib/moreoids.c @@ -127,6 +127,17 @@ static const SECOidData oids[] = { static const unsigned int numOids = (sizeof oids) / (sizeof oids[0]); +/* Fetch and register an oid if it hasn't been done already */ +void +SECU_cert_fetchOID(SECOidTag *data, const SECOidData *src) +{ + if (*data == SEC_OID_UNKNOWN) { + /* AddEntry does the right thing if someone else has already + * added the oid. (that is return that oid tag) */ + *data = SECOID_AddEntry(src); + } +} + SECStatus SECU_RegisterDynamicOids(void) { diff --git a/security/nss/cmd/lib/secutil.c b/security/nss/cmd/lib/secutil.c index 01a094e9dcaf..47b698355b32 100644 --- a/security/nss/cmd/lib/secutil.c +++ b/security/nss/cmd/lib/secutil.c @@ -1086,7 +1086,7 @@ typedef struct secuPBEParamsStr { SECAlgorithmID kdfAlg; } secuPBEParams; -SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate); +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) /* SECOID_PKCS5_PBKDF2 */ const SEC_ASN1Template secuKDF2Params[] = @@ -3614,8 +3614,8 @@ SECU_ParseSSLVersionRangeString(const char *input, colonPos = strchr(input, ':'); if (!colonPos) { - return SECFailure; PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; } colonIndex = colonPos - input; diff --git a/security/nss/cmd/lib/secutil.h b/security/nss/cmd/lib/secutil.h index c21e393c6e29..dc8c0324a73d 100644 --- a/security/nss/cmd/lib/secutil.h +++ b/security/nss/cmd/lib/secutil.h @@ -136,7 +136,7 @@ SECU_GetClientAuthData(void *arg, PRFileDesc *fd, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey); -extern PRBool SECU_GetWrapEnabled(); +extern PRBool SECU_GetWrapEnabled(void); extern void SECU_EnableWrap(PRBool enable); /* revalidate the cert and print information about cert verification @@ -293,6 +293,9 @@ extern SECStatus DER_PrettyPrint(FILE *out, SECItem *it, PRBool raw); extern char *SECU_SECModDBName(void); +/* Fetch and register an oid if it hasn't been done already */ +extern void SECU_cert_fetchOID(SECOidTag *data, const SECOidData *src); + extern SECStatus SECU_RegisterDynamicOids(void); /* Identifies hash algorithm tag by its string representation. */ diff --git a/security/nss/cmd/lowhashtest/lowhashtest.c b/security/nss/cmd/lowhashtest/lowhashtest.c index fdec32d321ec..8a128effbc63 100644 --- a/security/nss/cmd/lowhashtest/lowhashtest.c +++ b/security/nss/cmd/lowhashtest/lowhashtest.c @@ -398,8 +398,6 @@ Usage(char *progName) int main(int argc, char **argv) { - PLOptState *optstate; - PLOptStatus status; NSSLOWInitContext *initCtx; int rv = 0; /* counts the number of failures */ diff --git a/security/nss/cmd/pwdecrypt/pwdecrypt.c b/security/nss/cmd/pwdecrypt/pwdecrypt.c index f4056141aa4b..42f8a8618d15 100644 --- a/security/nss/cmd/pwdecrypt/pwdecrypt.c +++ b/security/nss/cmd/pwdecrypt/pwdecrypt.c @@ -5,7 +5,7 @@ /* * Test program for SDR (Secret Decoder Ring) functions. * - * $Id: pwdecrypt.c,v 1.8 2012/03/20 14:47:16 gerv%gerv.net Exp $ + * $Id: pwdecrypt.c,v 1.9 2012/12/12 19:25:36 wtc%google.com Exp $ */ #include "nspr.h" @@ -137,7 +137,6 @@ doDecrypt(char * dataString, FILE *outFile, FILE *logFile, secuPWData *pwdata) SECItem *decoded = NSSBase64_DecodeBuffer(NULL, NULL, dataString, strLen); SECStatus rv; int err; - unsigned int i; SECItem result = { siBuffer, NULL, 0 }; if ((decoded == NULL) || (decoded->len == 0)) { diff --git a/security/nss/lib/certhigh/ocsp.c b/security/nss/lib/certhigh/ocsp.c index 706b606a2cb6..6d1dfc04f353 100644 --- a/security/nss/lib/certhigh/ocsp.c +++ b/security/nss/lib/certhigh/ocsp.c @@ -6,7 +6,7 @@ * Implementation of OCSP services, for both client and server. * (XXX, really, mostly just for client right now, but intended to do both.) * - * $Id: ocsp.c,v 1.74.2.1 2012/12/12 16:38:39 wtc%google.com Exp $ + * $Id: ocsp.c,v 1.76 2012/12/12 19:29:40 wtc%google.com Exp $ */ #include "prerror.h" @@ -156,7 +156,7 @@ ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, int64 time); #define NSS_HAVE_GETENV 1 #endif -static PRBool wantOcspTrace() +static PRBool wantOcspTrace(void) { static PRBool firstTime = PR_TRUE; static PRBool wantTrace = PR_FALSE; @@ -504,7 +504,7 @@ ocsp_MakeCacheEntryMostRecent(OCSPCacheData *cache, OCSPCacheItem *new_most_rece } static PRBool -ocsp_IsCacheDisabled() +ocsp_IsCacheDisabled(void) { /* * maxCacheEntries == 0 means unlimited cache entries @@ -592,7 +592,7 @@ ocsp_CheckCacheSize(OCSPCacheData *cache) } SECStatus -CERT_ClearOCSPCache() +CERT_ClearOCSPCache(void) { OCSP_TRACE(("OCSP CERT_ClearOCSPCache\n")); PR_EnterMonitor(OCSP_Global.monitor); @@ -953,7 +953,7 @@ SECStatus OCSP_ShutdownGlobal(void) * A return value of NULL means: * The application did not register it's own HTTP client. */ -const SEC_HttpClientFcn *SEC_GetRegisteredHttpClient() +const SEC_HttpClientFcn *SEC_GetRegisteredHttpClient(void) { const SEC_HttpClientFcn *retval; @@ -1940,7 +1940,7 @@ loser: } static CERTOCSPRequest * -ocsp_prepareEmptyOCSPRequest() +ocsp_prepareEmptyOCSPRequest(void) { PRArenaPool *arena = NULL; CERTOCSPRequest *request = NULL; @@ -4686,7 +4686,7 @@ ocsp_GetCachedOCSPResponseStatusIfFresh(CERTOCSPCertID *certID, } PRBool -ocsp_FetchingFailureIsVerificationFailure() +ocsp_FetchingFailureIsVerificationFailure(void) { PRBool isFailure; diff --git a/security/nss/lib/certhigh/ocsp.h b/security/nss/lib/certhigh/ocsp.h index d901be312682..432bd19afb72 100644 --- a/security/nss/lib/certhigh/ocsp.h +++ b/security/nss/lib/certhigh/ocsp.h @@ -5,7 +5,7 @@ /* * Interface to the OCSP implementation. * - * $Id: ocsp.h,v 1.23.2.1 2012/12/12 16:38:39 wtc%google.com Exp $ + * $Id: ocsp.h,v 1.24 2012/12/12 16:03:44 wtc%google.com Exp $ */ #ifndef _OCSP_H_ diff --git a/security/nss/lib/certhigh/ocspi.h b/security/nss/lib/certhigh/ocspi.h index d4d369ca5bb9..c33f7f8c7ec5 100644 --- a/security/nss/lib/certhigh/ocspi.h +++ b/security/nss/lib/certhigh/ocspi.h @@ -4,7 +4,7 @@ /* * ocspi.h - NSS internal interfaces to OCSP code * - * $Id: ocspi.h,v 1.12 2012/04/25 14:49:27 gerv%gerv.net Exp $ + * $Id: ocspi.h,v 1.13 2012/12/12 19:29:40 wtc%google.com Exp $ */ #ifndef _OCSPI_H_ @@ -135,6 +135,6 @@ ocsp_GetResponderLocation(CERTCertDBHandle *handle, * revoked cert status. */ PRBool -ocsp_FetchingFailureIsVerificationFailure(); +ocsp_FetchingFailureIsVerificationFailure(void); #endif /* _OCSPI_H_ */ diff --git a/security/nss/lib/certhigh/ocspt.h b/security/nss/lib/certhigh/ocspt.h index f111ab96f803..09a8f17729cc 100644 --- a/security/nss/lib/certhigh/ocspt.h +++ b/security/nss/lib/certhigh/ocspt.h @@ -5,7 +5,7 @@ /* * Public header for exported OCSP types. * - * $Id: ocspt.h,v 1.11.2.1 2012/12/12 16:38:39 wtc%google.com Exp $ + * $Id: ocspt.h,v 1.12 2012/12/12 16:03:44 wtc%google.com Exp $ */ #ifndef _OCSPT_H_ diff --git a/security/nss/lib/certhigh/ocspti.h b/security/nss/lib/certhigh/ocspti.h index 6afa979caaed..41df43e8a327 100644 --- a/security/nss/lib/certhigh/ocspti.h +++ b/security/nss/lib/certhigh/ocspti.h @@ -5,7 +5,7 @@ /* * Private header defining OCSP types. * - * $Id: ocspti.h,v 1.8.2.1 2012/12/12 16:38:39 wtc%google.com Exp $ + * $Id: ocspti.h,v 1.9 2012/12/12 16:03:44 wtc%google.com Exp $ */ #ifndef _OCSPTI_H_ diff --git a/security/nss/lib/freebl/drbg.c b/security/nss/lib/freebl/drbg.c index 1503fc0dcd07..aa6d12859df6 100644 --- a/security/nss/lib/freebl/drbg.c +++ b/security/nss/lib/freebl/drbg.c @@ -1,7 +1,7 @@ /* 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/. */ -/* $Id: drbg.c,v 1.11 2012/06/28 17:55:05 rrelyea%redhat.com Exp $ */ +/* $Id: drbg.c,v 1.12 2012/12/12 19:22:39 wtc%google.com Exp $ */ #ifdef FREEBL_NO_DEPEND #include "stubs.h" @@ -470,7 +470,7 @@ RNG_RNGInit(void) /* Allow only one call to initialize the context */ PR_CallOnce(&coRNGInit, rng_init); /* Make sure there is a context */ - return (globalrng != NULL) ? PR_SUCCESS : PR_FAILURE; + return (globalrng != NULL) ? SECSuccess : SECFailure; } /* diff --git a/security/nss/lib/freebl/loader.c b/security/nss/lib/freebl/loader.c index 101ab1be8e25..4f274e49b3da 100644 --- a/security/nss/lib/freebl/loader.c +++ b/security/nss/lib/freebl/loader.c @@ -4,7 +4,7 @@ * 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/. */ -/* $Id: loader.c,v 1.57 2012/06/28 17:55:05 rrelyea%redhat.com Exp $ */ +/* $Id: loader.c,v 1.58 2012/12/13 22:47:15 wtc%google.com Exp $ */ #include "loader.h" #include "prmem.h" @@ -1851,10 +1851,10 @@ PQG_ParamGenV2( unsigned int L, unsigned int N, unsigned int seedBytes, return (vector->p_PQG_ParamGenV2)(L, N, seedBytes, pParams, pVfy); } -PRBool +SECStatus PRNGTEST_RunHealthTests(void) { if (!vector && PR_SUCCESS != freebl_RunLoaderOnce()) - return PR_FALSE; + return SECFailure; return vector->p_PRNGTEST_RunHealthTests(); } diff --git a/security/nss/lib/freebl/pqg.c b/security/nss/lib/freebl/pqg.c index 3c4db48ae1fe..71284025ec27 100644 --- a/security/nss/lib/freebl/pqg.c +++ b/security/nss/lib/freebl/pqg.c @@ -5,7 +5,7 @@ /* * PQG parameter generation/verification. Based on FIPS 186-3. * - * $Id: pqg.c,v 1.25 2012/10/11 00:18:23 rrelyea%redhat.com Exp $ + * $Id: pqg.c,v 1.26 2012/12/13 22:47:15 wtc%google.com Exp $ */ #ifdef FREEBL_NO_DEPEND #include "stubs.h" @@ -260,7 +260,7 @@ PQG_GetHashType(const PQGParams *params) if (params == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); - return SECFailure; + return HASH_AlgNULL; } L = PQG_GetLength(¶ms->prime)*BITS_PER_BYTE; diff --git a/security/nss/lib/freebl/unix_rand.c b/security/nss/lib/freebl/unix_rand.c index fe97a4d2a593..086f1050e528 100644 --- a/security/nss/lib/freebl/unix_rand.c +++ b/security/nss/lib/freebl/unix_rand.c @@ -193,11 +193,6 @@ GiveSystemInfo(void) #if defined(__sun) #if defined(__svr4) || defined(SVR4) #include -#include -#include - -int gettimeofday(struct timeval *); -int gethostname(char *, int); #define getdtablesize() sysconf(_SC_OPEN_MAX) @@ -672,11 +667,7 @@ size_t RNG_GetNoise(void *buf, size_t maxbytes) n = GetHighResClock(buf, maxbytes); maxbytes -= n; -#if defined(__sun) && (defined(_svr4) || defined(SVR4)) || defined(sony) - (void)gettimeofday(&tv); -#else (void)gettimeofday(&tv, 0); -#endif c = CopyLowBits((char*)buf+n, maxbytes, &tv.tv_usec, sizeof(tv.tv_usec)); n += c; maxbytes -= c; diff --git a/security/nss/lib/nss/nss.h b/security/nss/lib/nss/nss.h index 4b164ea82cc2..74eeddfe96a8 100644 --- a/security/nss/lib/nss/nss.h +++ b/security/nss/lib/nss/nss.h @@ -4,7 +4,7 @@ * 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/. */ -/* $Id: nss.h,v 1.100.2.1 2012/12/13 19:11:46 wtc%google.com Exp $ */ +/* $Id: nss.h,v 1.101 2012/12/10 23:39:39 wtc%google.com Exp $ */ #ifndef __nss_h_ #define __nss_h_ @@ -34,12 +34,12 @@ * The format of the version string should be * ".[.[.]][ ][ ]" */ -#define NSS_VERSION "3.14.1.0" _NSS_ECC_STRING _NSS_CUSTOMIZED +#define NSS_VERSION "3.14.2.0" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta" #define NSS_VMAJOR 3 #define NSS_VMINOR 14 -#define NSS_VPATCH 1 +#define NSS_VPATCH 2 #define NSS_VBUILD 0 -#define NSS_BETA PR_FALSE +#define NSS_BETA PR_TRUE #ifndef RC_INVOKED diff --git a/security/nss/lib/pkcs7/p7decode.c b/security/nss/lib/pkcs7/p7decode.c index 51358c393788..d0d02d75e8c5 100644 --- a/security/nss/lib/pkcs7/p7decode.c +++ b/security/nss/lib/pkcs7/p7decode.c @@ -5,7 +5,7 @@ /* * PKCS7 decoding, verification. * - * $Id: p7decode.c,v 1.30 2012/11/27 22:48:08 bsmith%mozilla.com Exp $ + * $Id: p7decode.c,v 1.31 2012/12/12 19:25:36 wtc%google.com Exp $ */ #include "p7local.h" @@ -407,7 +407,6 @@ sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx, PK11SymKey *bulkkey = NULL; SECOidTag keyalgtag, bulkalgtag, encalgtag; PK11SlotInfo *slot = NULL; - int bulkLength = 0; if (recipientinfos == NULL || recipientinfos[0] == NULL) { p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; diff --git a/security/nss/lib/pki/pki3hack.c b/security/nss/lib/pki/pki3hack.c index c70c04b85179..16b4cdedbd6b 100644 --- a/security/nss/lib/pki/pki3hack.c +++ b/security/nss/lib/pki/pki3hack.c @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifdef DEBUG -static const char CVS_ID[] = "@(#) $RCSfile: pki3hack.c,v $ $Revision: 1.109 $ $Date: 2012/07/27 21:41:52 $"; +static const char CVS_ID[] = "@(#) $RCSfile: pki3hack.c,v $ $Revision: 1.110 $ $Date: 2012/12/12 19:22:40 $"; #endif /* DEBUG */ /* @@ -1092,7 +1092,7 @@ STAN_ChangeCertTrust(CERTCertificate *cc, CERTCertTrust *trust) nssPKIObject *pkiob; if (c == NULL) { - return SECFailure; + return PR_FAILURE; } oldTrust = nssTrust_GetCERTCertTrustForCert(c, cc); if (oldTrust) { diff --git a/security/nss/lib/smime/cmsasn1.c b/security/nss/lib/smime/cmsasn1.c index b8ddc6655f30..3cd73f6ae4d3 100644 --- a/security/nss/lib/smime/cmsasn1.c +++ b/security/nss/lib/smime/cmsasn1.c @@ -5,7 +5,7 @@ /* * CMS ASN.1 templates * - * $Id: cmsasn1.c,v 1.11 2012/04/25 14:50:08 gerv%gerv.net Exp $ + * $Id: cmsasn1.c,v 1.12 2012/12/13 22:46:04 wtc%google.com Exp $ */ #include "cmslocal.h" @@ -453,13 +453,13 @@ const SEC_ASN1Template NSSCMSGenericWrapperDataTemplate[] = { NSSCMSEncapsulatedContentInfoTemplate }, }; -SEC_ASN1_CHOOSER_IMPLEMENT(NSSCMSGenericWrapperDataTemplate); +SEC_ASN1_CHOOSER_IMPLEMENT(NSSCMSGenericWrapperDataTemplate) const SEC_ASN1Template NSS_PointerToCMSGenericWrapperDataTemplate[] = { { SEC_ASN1_POINTER, 0, NSSCMSGenericWrapperDataTemplate } }; -SEC_ASN1_CHOOSER_IMPLEMENT(NSS_PointerToCMSGenericWrapperDataTemplate); +SEC_ASN1_CHOOSER_IMPLEMENT(NSS_PointerToCMSGenericWrapperDataTemplate) /* ----------------------------------------------------------------------------- * diff --git a/security/nss/lib/softoken/legacydb/pcertdb.c b/security/nss/lib/softoken/legacydb/pcertdb.c index 0a680e6edb18..7e4214ad8c5d 100644 --- a/security/nss/lib/softoken/legacydb/pcertdb.c +++ b/security/nss/lib/softoken/legacydb/pcertdb.c @@ -5,7 +5,7 @@ /* * Permanent Certificate database handling code * - * $Id: pcertdb.c,v 1.13 2012/04/25 14:50:11 gerv%gerv.net Exp $ + * $Id: pcertdb.c,v 1.14 2012/12/12 19:25:36 wtc%google.com Exp $ */ #include "lowkeyti.h" #include "pcert.h" @@ -4954,7 +4954,7 @@ DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb) refCount = --cert->referenceCount; nsslowcert_UnlockCertRefCount(cert); - if ( ( refCount == 0 ) ) { + if ( refCount == 0 ) { certDBEntryCert *entry = cert->dbEntry; if ( entry ) { diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c index 2d0fcf4c71e1..ee38d8d21e4b 100644 --- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -1173,7 +1173,6 @@ CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, if (context->padDataLength > 0) { *pulLastPartLen = context->padDataLength; } - rv = SECSuccess; goto finish; } @@ -1184,13 +1183,26 @@ CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, * buffer!!! */ rv = (*context->update)(context->cipherInfo, pLastPart, &outlen, maxout, context->padBuf, context->blockSize); - if (rv == SECSuccess) { + if (rv != SECSuccess) { + crv = sftk_MapDecryptError(PORT_GetError()); + } else { unsigned int padSize = (unsigned int) pLastPart[context->blockSize-1]; if ((padSize > context->blockSize) || (padSize == 0)) { - rv = SECFailure; + crv = CKR_ENCRYPTED_DATA_INVALID; } else { - *pulLastPartLen = outlen - padSize; + unsigned int i; + unsigned int badPadding = 0; /* used as a boolean */ + for (i = 0; i < padSize; i++) { + badPadding |= + (unsigned int) pLastPart[context->blockSize-1-i] ^ + padSize; + } + if (badPadding) { + crv = CKR_ENCRYPTED_DATA_INVALID; + } else { + *pulLastPartLen = outlen - padSize; + } } } } @@ -1199,7 +1211,7 @@ CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, sftk_TerminateOp( session, SFTK_DECRYPT, context ); finish: sftk_FreeSession(session); - return (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); + return crv; } /* NSC_Decrypt decrypts encrypted data in a single part. */ @@ -1249,11 +1261,21 @@ CK_RV NSC_Decrypt(CK_SESSION_HANDLE hSession, /* XXX need to do MUCH better error mapping than this. */ crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); if (rv == SECSuccess && context->doPad) { - CK_ULONG padding = pData[outlen - 1]; + unsigned int padding = pData[outlen - 1]; if (padding > context->blockSize || !padding) { crv = CKR_ENCRYPTED_DATA_INVALID; - } else - outlen -= padding; + } else { + unsigned int i; + unsigned int badPadding = 0; /* used as a boolean */ + for (i = 0; i < padding; i++) { + badPadding |= (unsigned int) pData[outlen - 1 - i] ^ padding; + } + if (badPadding) { + crv = CKR_ENCRYPTED_DATA_INVALID; + } else { + outlen -= padding; + } + } } *pulDataLen = (CK_ULONG) outlen; sftk_TerminateOp( session, SFTK_DECRYPT, context ); diff --git a/security/nss/lib/softoken/sdb.c b/security/nss/lib/softoken/sdb.c index c6ff11037c88..8ab757c45f8f 100644 --- a/security/nss/lib/softoken/sdb.c +++ b/security/nss/lib/softoken/sdb.c @@ -30,8 +30,11 @@ #include "prenv.h" #include "prsystem.h" /* for PR_GetDirectorySeparator() */ #include "sys/stat.h" -#if defined (_WIN32) +#if defined(_WIN32) #include +#include +#elif defined(XP_UNIX) +#include #endif #ifdef SQLITE_UNSAFE_THREADS @@ -187,106 +190,68 @@ sdb_done(int err, int *count) } /* - * - * strdup limited to 'n' bytes. (Note: len of file is assumed to be >= len) - * - * We don't have a PORT_ version of this function, - * I suspect it's only normally available in glib, + * find out where sqlite stores the temp tables. We do this by replicating + * the logic from sqlite. */ +#if defined(_WIN32) static char * -sdb_strndup(const char *file, int len) +sdb_getTempDir(void) { - char *result = PORT_Alloc(len+1); + /* sqlite uses sqlite3_temp_directory if it is not NULL. We don't have + * access to sqlite3_temp_directory because it is not exported from + * sqlite3.dll. Assume sqlite3_win32_set_directory isn't called and + * sqlite3_temp_directory is NULL. + */ + char path[MAX_PATH]; + DWORD rv; + size_t len; - if (result == NULL) { - return result; - } - - PORT_Memcpy(result, file, len); - result[len] = 0; - return result; + rv = GetTempPathA(MAX_PATH, path); + if (rv > MAX_PATH || rv == 0) + return NULL; + len = strlen(path); + if (len == 0) + return NULL; + /* The returned string ends with a backslash, for example, "C:\TEMP\". */ + if (path[len - 1] == '\\') + path[len - 1] = '\0'; + return PORT_Strdup(path); } - -/* - * call back from sqlite3_exec("Pragma database_list"). Looks for the - * temp directory, then return the file the temp directory is stored - * at. */ -static int -sdb_getTempDirCallback(void *arg, int columnCount, char **cval, char **cname) -{ - int i; - int found = 0; - char *file = NULL; - char *end, *dir; - char dirsep; - - /* we've already found the temp directory, don't look at any more records*/ - if (*(char **)arg) { - return SQLITE_OK; - } - - /* look at the columns to see if this record is the temp database, - * and does it say where it is stored */ - for (i=0; i < columnCount; i++) { - if (PORT_Strcmp(cname[i],"name") == 0) { - if (PORT_Strcmp(cval[i], "temp") == 0) { - found++; - continue; - } - } - if (PORT_Strcmp(cname[i],"file") == 0) { - if (cval[i] && (*cval[i] != 0)) { - file = cval[i]; - } - } - } - - /* if we couldn't find it, ask for the next record */ - if (!found || !file) { - return SQLITE_OK; - } - - /* drop of the database file name and just return the directory */ - dirsep = PR_GetDirectorySeparator(); - end = PORT_Strrchr(file, dirsep); - if (!end) { - return SQLITE_OK; - } - dir = sdb_strndup(file, end-file); - - *(char **)arg = dir; - return SQLITE_OK; -} - -/* - * find out where sqlite stores the temp tables. We do this by creating - * a temp table, then looking for the database name that sqlite3 creates. - */ +#elif defined(XP_UNIX) static char * -sdb_getTempDir(sqlite3 *sqlDB) +sdb_getTempDir(void) { - char *tempDir = NULL; - int sqlerr; + const char *azDirs[] = { + NULL, + NULL, + "/var/tmp", + "/usr/tmp", + "/tmp", + NULL /* List terminator */ + }; + unsigned int i; + struct stat buf; + const char *zDir = NULL; - /* create a temporary table */ - sqlerr = sqlite3_exec(sqlDB, "CREATE TEMPORARY TABLE myTemp (id)", - NULL, 0, NULL); - if (sqlerr != SQLITE_OK) { - return NULL; + azDirs[0] = sqlite3_temp_directory; + azDirs[1] = getenv("TMPDIR"); + + for (i = 0; i < PR_ARRAY_SIZE(azDirs); i++) { + zDir = azDirs[i]; + if (zDir == NULL) continue; + if (stat(zDir, &buf)) continue; + if (!S_ISDIR(buf.st_mode)) continue; + if (access(zDir, 07)) continue; + break; } - /* look for through the database list for the temp directory */ - sqlerr = sqlite3_exec(sqlDB, "PRAGMA database_list", - sdb_getTempDirCallback, &tempDir, NULL); - /* drop the temp table we created */ - sqlite3_exec(sqlDB, "DROP TABLE myTemp", NULL, 0, NULL); - - if (sqlerr != SQLITE_OK) { - return NULL; - } - return tempDir; + if (zDir == NULL) + return NULL; + return PORT_Strdup(zDir); } - +#else +#error "sdb_getTempDir not implemented" +#endif /* * Map SQL_LITE errors to PKCS #11 errors as best we can. @@ -1827,7 +1792,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, * is to check for the existance of a local file compared to the same * check in the temp directory. If the temp directory is faster, cache * the database there. */ - tempDir = sdb_getTempDir(sqlDB); + tempDir = sdb_getTempDir(); if (tempDir) { tempOps = sdb_measureAccess(tempDir); PORT_Free(tempDir); diff --git a/security/nss/lib/softoken/softkver.h b/security/nss/lib/softoken/softkver.h index a2b006ece5d7..c8fa85e79759 100644 --- a/security/nss/lib/softoken/softkver.h +++ b/security/nss/lib/softoken/softkver.h @@ -25,11 +25,11 @@ * The format of the version string should be * ".[.[.]][ ][ ]" */ -#define SOFTOKEN_VERSION "3.14.1.0" SOFTOKEN_ECC_STRING +#define SOFTOKEN_VERSION "3.14.2.0" SOFTOKEN_ECC_STRING " Beta" #define SOFTOKEN_VMAJOR 3 #define SOFTOKEN_VMINOR 14 -#define SOFTOKEN_VPATCH 1 +#define SOFTOKEN_VPATCH 2 #define SOFTOKEN_VBUILD 0 -#define SOFTOKEN_BETA PR_FALSE +#define SOFTOKEN_BETA PR_TRUE #endif /* _SOFTKVER_H_ */ diff --git a/security/nss/lib/sqlite/README b/security/nss/lib/sqlite/README index ff9b5bed2955..dbb9048f5dc4 100644 --- a/security/nss/lib/sqlite/README +++ b/security/nss/lib/sqlite/README @@ -1,6 +1,3 @@ -This is SQLite 3.6.22. +This is SQLite 3.7.14.1. Local changes: - -1. Allow System V one-argument version of gettimeofday when compiled with --D_SVID_GETTOD on Solaris. See CVS revision 1.6. diff --git a/security/nss/lib/sqlite/config.mk b/security/nss/lib/sqlite/config.mk index 3af6386b5faa..c7b93ed3910d 100644 --- a/security/nss/lib/sqlite/config.mk +++ b/security/nss/lib/sqlite/config.mk @@ -34,4 +34,14 @@ ifeq ($(OS_TARGET),Darwin) # to the linker.) Apple builds the system libsqlite3.dylib with these # version numbers, so we use the same to be compatible. DARWIN_DYLIB_VERSIONS = -compatibility_version 9 -current_version 9.6 + +# The SQLite code that uses the Apple zone allocator calls +# OSAtomicCompareAndSwapPtrBarrier, which is only available on Mac OS X 10.5 +# (Darwin 9.0) and later. Define SQLITE_WITHOUT_ZONEMALLOC to disable +# that code for older versions of Mac OS X. See bug 820374. +DARWIN_VER_MAJOR := $(shell uname -r | cut -f1 -d.) +DARWIN_LT_9 := $(shell [ $(DARWIN_VER_MAJOR) -lt 9 ] && echo true) +ifeq ($(DARWIN_LT_9),true) +OS_CFLAGS += -DSQLITE_WITHOUT_ZONEMALLOC endif +endif # Darwin diff --git a/security/nss/lib/sqlite/sqlite.def b/security/nss/lib/sqlite/sqlite.def index 5051f3705633..00fa623abff0 100644 --- a/security/nss/lib/sqlite/sqlite.def +++ b/security/nss/lib/sqlite/sqlite.def @@ -74,6 +74,7 @@ sqlite3_errmsg16; sqlite3_exec; sqlite3_expired; sqlite3_extended_result_codes; +sqlite3_file_control; sqlite3_finalize; sqlite3_free; sqlite3_free_table; @@ -119,6 +120,7 @@ sqlite3_set_auxdata; sqlite3_sleep; sqlite3_snprintf; sqlite3_step; +;;sqlite3_temp_directory DATA ; sqlite3_thread_cleanup; sqlite3_total_changes; sqlite3_trace; @@ -139,6 +141,7 @@ sqlite3_value_text16le; sqlite3_value_type; sqlite3_version; sqlite3_vmprintf; +sqlite3_wal_checkpoint; ;+ local: ;+ *; ;+}; diff --git a/security/nss/lib/sqlite/sqlite3.c b/security/nss/lib/sqlite/sqlite3.c index e7b20ca773a0..202f65c5c381 100644 --- a/security/nss/lib/sqlite/sqlite3.c +++ b/security/nss/lib/sqlite/sqlite3.c @@ -1,10 +1,10 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.6.22. By combining all the individual C code files into this -** single large file, the entire code can be compiled as a one translation +** version 3.7.15. By combining all the individual C code files into this +** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements -** of 5% are more are commonly seen when SQLite is compiled as a single +** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other @@ -190,9 +190,17 @@ # define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 #endif +/* +** The default number of frames to accumulate in the log file before +** checkpointing the database in WAL mode. +*/ +#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT +# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000 +#endif + /* ** The maximum number of attached databases. This must be between 0 -** and 30. The upper bound on 30 is because a 32-bit integer bitmap +** and 62. The upper bound on 62 is because a 64-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED @@ -207,20 +215,21 @@ # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif -/* Maximum page size. The upper bound on this value is 32768. This a limit -** imposed by the necessity of storing the value in a 2-byte unsigned integer -** and the fact that the page size must be a power of 2. +/* Maximum page size. The upper bound on this value is 65536. This a limit +** imposed by the use of 16-bit offsets within each page. ** -** If this limit is changed, then the compiled library is technically -** incompatible with an SQLite library compiled with a different limit. If -** a process operating on a database with a page-size of 65536 bytes -** crashes, then an instance of SQLite compiled with the default page-size -** limit will not be able to rollback the aborted transaction. This could -** lead to database corruption. +** Earlier versions of SQLite allowed the user to change this value at +** compile time. This is no longer permitted, on the grounds that it creates +** a library that is technically incompatible with an SQLite library +** compiled with a different limit. If a process operating on a database +** with a page-size of 65536 bytes crashes, then an instance of SQLite +** compiled with the default page-size limit will not be able to rollback +** the aborted transaction. This could lead to database corruption. */ -#ifndef SQLITE_MAX_PAGE_SIZE -# define SQLITE_MAX_PAGE_SIZE 32768 +#ifdef SQLITE_MAX_PAGE_SIZE +# undef SQLITE_MAX_PAGE_SIZE #endif +#define SQLITE_MAX_PAGE_SIZE 65536 /* @@ -307,54 +316,63 @@ #include #endif -#define SQLITE_INDEX_SAMPLES 10 - /* -** This macro is used to "hide" some ugliness in casting an int -** value to a ptr value under the MSVC 64-bit compiler. Casting -** non 64-bit values to ptr types results in a "hard" error with -** the MSVC 64-bit compiler which this attempts to avoid. +** The following macros are used to cast pointers to integers and +** integers to pointers. The way you do this varies from one compiler +** to the next, so we have developed the following set of #if statements +** to generate appropriate macros for a wide range of compilers. ** -** A simple compiler pragma or casting sequence could not be found -** to correct this in all situations, so this macro was introduced. -** -** It could be argued that the intptr_t type could be used in this -** case, but that type is not available on all compilers, or -** requires the #include of specific headers which differs between -** platforms. +** The correct "ANSI" way to do this is to use the intptr_t type. +** Unfortunately, that typedef is not available on all compilers, or +** if it is available, it requires an #include of specific headers +** that vary from one machine to the next. ** ** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on ** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). ** So we have to define the macros in different ways depending on the ** compiler. */ -#if defined(__GNUC__) -# if defined(HAVE_STDINT_H) -# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) -# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) -# else -# define SQLITE_INT_TO_PTR(X) ((void*)(X)) -# define SQLITE_PTR_TO_INT(X) ((int)(X)) -# endif -#else -# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) -# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) +#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ +# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) +# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) +#elif !defined(__GNUC__) /* Works for compilers other than LLVM */ +# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) +# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) +#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ +# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) +# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) +#else /* Generates a warning - but it always works */ +# define SQLITE_INT_TO_PTR(X) ((void*)(X)) +# define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif - /* -** The SQLITE_THREADSAFE macro must be defined as either 0 or 1. +** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2. +** 0 means mutexes are permanently disable and the library is never +** threadsafe. 1 means the library is serialized which is the highest +** level of threadsafety. 2 means the libary is multithreaded - multiple +** threads can use SQLite as long as no two threads try to use the same +** database connection at the same time. +** ** Older versions of SQLite used an optional THREADSAFE macro. -** We support that for legacy +** We support that for legacy. */ #if !defined(SQLITE_THREADSAFE) #if defined(THREADSAFE) # define SQLITE_THREADSAFE THREADSAFE #else -# define SQLITE_THREADSAFE 1 +# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */ #endif #endif +/* +** Powersafe overwrite is on by default. But can be turned off using +** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option. +*/ +#ifndef SQLITE_POWERSAFE_OVERWRITE +# define SQLITE_POWERSAFE_OVERWRITE 1 +#endif + /* ** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. ** It determines whether or not the features related to @@ -370,24 +388,34 @@ ** specify which memory allocation subsystem to use. ** ** SQLITE_SYSTEM_MALLOC // Use normal system malloc() +** SQLITE_WIN32_MALLOC // Use Win32 native heap API +** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails ** SQLITE_MEMDEBUG // Debugging version of system malloc() -** SQLITE_MEMORY_SIZE // internal allocator #1 -** SQLITE_MMAP_HEAP_SIZE // internal mmap() allocator -** SQLITE_POW2_MEMORY_SIZE // internal power-of-two allocator +** +** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the +** assert() macro is enabled, each call into the Win32 native heap subsystem +** will cause HeapValidate to be called. If heap validation should fail, an +** assertion will be triggered. +** +** (Historical note: There used to be several other options, but we've +** pared it down to just these three.) ** ** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as ** the default. */ -#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\ - defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\ - defined(SQLITE_POW2_MEMORY_SIZE)>1 -# error "At most one of the following compile-time configuration options\ - is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG, SQLITE_MEMORY_SIZE,\ - SQLITE_MMAP_HEAP_SIZE, SQLITE_POW2_MEMORY_SIZE" +#if defined(SQLITE_SYSTEM_MALLOC) \ + + defined(SQLITE_WIN32_MALLOC) \ + + defined(SQLITE_ZERO_MALLOC) \ + + defined(SQLITE_MEMDEBUG)>1 +# error "Two or more of the following compile-time configuration options\ + are defined but at most one is allowed:\ + SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\ + SQLITE_ZERO_MALLOC" #endif -#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)+\ - defined(SQLITE_MEMORY_SIZE)+defined(SQLITE_MMAP_HEAP_SIZE)+\ - defined(SQLITE_POW2_MEMORY_SIZE)==0 +#if defined(SQLITE_SYSTEM_MALLOC) \ + + defined(SQLITE_WIN32_MALLOC) \ + + defined(SQLITE_ZERO_MALLOC) \ + + defined(SQLITE_MEMDEBUG)==0 # define SQLITE_SYSTEM_MALLOC 1 #endif @@ -424,15 +452,22 @@ #endif /* -** Many people are failing to set -DNDEBUG=1 when compiling SQLite. -** Setting NDEBUG makes the code smaller and run faster. So the following -** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1 -** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out +** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that +** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true, +** make it true by defining or undefining NDEBUG. +** +** Setting NDEBUG makes the code smaller and run faster by disabling the +** number assert() statements in the code. So we want the default action +** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG +** is set. Thus NDEBUG becomes an opt-in rather than an opt-out ** feature. */ #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif +#if defined(NDEBUG) && defined(SQLITE_DEBUG) +# undef NDEBUG +#endif /* ** The testcase() macro is used to aid in coverage testing. When @@ -506,6 +541,13 @@ SQLITE_PRIVATE void sqlite3Coverage(int); # define NEVER(X) (X) #endif +/* +** Return true (non-zero) if the input is a integer that is too large +** to fit in 32-bits. This macro is used inside of various testcase() +** macros to verify that we have tested SQLite for large-file support. +*/ +#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0) + /* ** The macro unlikely() is a hint that surrounds a boolean ** expression that is usually false. Macro likely() surrounds @@ -621,7 +663,7 @@ extern "C" { ** ** Since version 3.6.18, SQLite source code has been stored in the ** Fossil configuration management -** system. ^The SQLITE_SOURCE_ID macro evalutes to +** system. ^The SQLITE_SOURCE_ID macro evaluates to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and an SHA1 @@ -631,13 +673,13 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.6.22" -#define SQLITE_VERSION_NUMBER 3006022 -#define SQLITE_SOURCE_ID "2010-01-05 15:30:36 28d0d7710761114a44a1a3a425a6883c661f06e7" +#define SQLITE_VERSION "3.7.15" +#define SQLITE_VERSION_NUMBER 3007015 +#define SQLITE_SOURCE_ID "2012-12-12 13:36:53 cd0b37c52658bfdf992b1e3dc467bae1835a94ae" /* ** CAPI3REF: Run-Time Library Version Numbers -** KEYWORDS: sqlite3_version +** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros @@ -659,9 +701,9 @@ extern "C" { ** function is provided for use in DLLs since DLL users usually do not have ** direct access to string constants within the DLL. ^The ** sqlite3_libversion_number() function returns an integer equal to -** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function a pointer -** to a string constant whose value is the same as the [SQLITE_SOURCE_ID] -** C preprocessor macro. +** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns +** a pointer to a string constant whose value is the same as the +** [SQLITE_SOURCE_ID] C preprocessor macro. ** ** See also: [sqlite_version()] and [sqlite_source_id()]. */ @@ -670,11 +712,38 @@ SQLITE_API const char *sqlite3_libversion(void); SQLITE_API const char *sqlite3_sourceid(void); SQLITE_API int sqlite3_libversion_number(void); +/* +** CAPI3REF: Run-Time Library Compilation Options Diagnostics +** +** ^The sqlite3_compileoption_used() function returns 0 or 1 +** indicating whether the specified option was defined at +** compile time. ^The SQLITE_ prefix may be omitted from the +** option name passed to sqlite3_compileoption_used(). +** +** ^The sqlite3_compileoption_get() function allows iterating +** over the list of options that were defined at compile time by +** returning the N-th compile time option string. ^If N is out of range, +** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ +** prefix is omitted from any strings returned by +** sqlite3_compileoption_get(). +** +** ^Support for the diagnostic functions sqlite3_compileoption_used() +** and sqlite3_compileoption_get() may be omitted by specifying the +** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. +** +** See also: SQL functions [sqlite_compileoption_used()] and +** [sqlite_compileoption_get()] and the [compile_options pragma]. +*/ +#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS +SQLITE_API int sqlite3_compileoption_used(const char *zOptName); +SQLITE_API const char *sqlite3_compileoption_get(int N); +#endif + /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if -** SQLite was compiled mutexing code omitted due to the +** SQLite was compiled with mutexing code omitted due to the ** [SQLITE_THREADSAFE] compile-time option being set to 0. ** ** SQLite can be compiled with or without mutexes. When @@ -716,7 +785,8 @@ SQLITE_API int sqlite3_threadsafe(void); ** the opaque structure named "sqlite3". It is useful to think of an sqlite3 ** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and ** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] -** is its destructor. There are many other interfaces (such as +** and [sqlite3_close_v2()] are its destructors. There are many other +** interfaces (such as ** [sqlite3_prepare_v2()], [sqlite3_create_function()], and ** [sqlite3_busy_timeout()] to name but three) that are methods on an ** sqlite3 object. @@ -763,28 +833,46 @@ typedef sqlite_uint64 sqlite3_uint64; /* ** CAPI3REF: Closing A Database Connection ** -** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. -** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is -** successfullly destroyed and all associated resources are deallocated. +** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors +** for the [sqlite3] object. +** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if +** the [sqlite3] object is successfully destroyed and all associated +** resources are deallocated. ** -** Applications must [sqlite3_finalize | finalize] all [prepared statements] -** and [sqlite3_blob_close | close] all [BLOB handles] associated with -** the [sqlite3] object prior to attempting to close the object. ^If +** ^If the database connection is associated with unfinalized prepared +** statements or unfinished sqlite3_backup objects then sqlite3_close() +** will leave the database connection open and return [SQLITE_BUSY]. +** ^If sqlite3_close_v2() is called with unfinalized prepared statements +** and unfinished sqlite3_backups, then the database connection becomes +** an unusable "zombie" which will automatically be deallocated when the +** last prepared statement is finalized or the last sqlite3_backup is +** finished. The sqlite3_close_v2() interface is intended for use with +** host languages that are garbage collected, and where the order in which +** destructors are called is arbitrary. +** +** Applications should [sqlite3_finalize | finalize] all [prepared statements], +** [sqlite3_blob_close | close] all [BLOB handles], and +** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated +** with the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close() is called on a [database connection] that still has -** outstanding [prepared statements] or [BLOB handles], then it returns -** SQLITE_BUSY. +** outstanding [prepared statements], [BLOB handles], and/or +** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation +** of resources is deferred until all [prepared statements], [BLOB handles], +** and [sqlite3_backup] objects are also destroyed. ** -** ^If [sqlite3_close()] is invoked while a transaction is open, +** ^If an [sqlite3] object is destroyed while a transaction is open, ** the transaction is automatically rolled back. ** -** The C parameter to [sqlite3_close(C)] must be either a NULL +** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)] +** must be either a NULL ** pointer or an [sqlite3] object pointer obtained ** from [sqlite3_open()], [sqlite3_open16()], or ** [sqlite3_open_v2()], and not previously closed. -** ^Calling sqlite3_close() with a NULL pointer argument is a -** harmless no-op. +** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer +** argument is a harmless no-op. */ -SQLITE_API int sqlite3_close(sqlite3 *); +SQLITE_API int sqlite3_close(sqlite3*); +SQLITE_API int sqlite3_close_v2(sqlite3*); /* ** The type for a callback function. @@ -807,7 +895,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); ** argument. ^If the callback function of the 3rd argument to ** sqlite3_exec() is not NULL, then it is invoked for each result row ** coming out of the evaluated SQL statements. ^The 4th argument to -** to sqlite3_exec() is relayed through to the 1st argument of each +** sqlite3_exec() is relayed through to the 1st argument of each ** callback invocation. ^If the callback pointer to sqlite3_exec() ** is NULL, then no callback is ever invoked and result rows are ** ignored. @@ -868,11 +956,12 @@ SQLITE_API int sqlite3_exec( ** KEYWORDS: {result code} {result codes} ** ** Many SQLite functions return an integer result code from the set shown -** here in order to indicates success or failure. +** here in order to indicate success or failure. ** ** New error codes may be added in future versions of SQLite. ** -** See also: [SQLITE_IOERR_READ | extended result codes] +** See also: [SQLITE_IOERR_READ | extended result codes], +** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes]. */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ @@ -887,10 +976,10 @@ SQLITE_API int sqlite3_exec( #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ -#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ -#define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ #define SQLITE_EMPTY 16 /* Database is empty */ #define SQLITE_SCHEMA 17 /* The database schema changed */ #define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ @@ -946,21 +1035,37 @@ SQLITE_API int sqlite3_exec( #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) #define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) #define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) -#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) ) +#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18<<8)) +#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19<<8)) +#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8)) +#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8)) +#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) +#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) +#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) +#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) +#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8)) +#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8)) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and -** in the 4th parameter to the xOpen method of the -** [sqlite3_vfs] object. +** in the 4th parameter to the [sqlite3_vfs.xOpen] method. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ #define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ +#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ +#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ #define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ #define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ @@ -972,12 +1077,15 @@ SQLITE_API int sqlite3_exec( #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_WAL 0x00080000 /* VFS only */ + +/* Reserved: 0x00F00000 */ /* ** CAPI3REF: Device Characteristics ** -** The xDeviceCapabilities method of the [sqlite3_io_methods] -** object returns an integer which is a vector of the these +** The xDeviceCharacteristics method of the [sqlite3_io_methods] +** object returns an integer which is a vector of these ** bit values expressing I/O characteristics of the mass storage ** device that holds the file that the [sqlite3_io_methods] ** refers to. @@ -991,19 +1099,25 @@ SQLITE_API int sqlite3_exec( ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls -** to xWrite(). +** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that +** after reboot following a crash or power loss, the only bytes in a +** file that were written at the application level might have changed +** and that adjacent bytes, even bytes within the same sector are +** guaranteed to be unchanged. */ -#define SQLITE_IOCAP_ATOMIC 0x00000001 -#define SQLITE_IOCAP_ATOMIC512 0x00000002 -#define SQLITE_IOCAP_ATOMIC1K 0x00000004 -#define SQLITE_IOCAP_ATOMIC2K 0x00000008 -#define SQLITE_IOCAP_ATOMIC4K 0x00000010 -#define SQLITE_IOCAP_ATOMIC8K 0x00000020 -#define SQLITE_IOCAP_ATOMIC16K 0x00000040 -#define SQLITE_IOCAP_ATOMIC32K 0x00000080 -#define SQLITE_IOCAP_ATOMIC64K 0x00000100 -#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 -#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 +#define SQLITE_IOCAP_ATOMIC 0x00000001 +#define SQLITE_IOCAP_ATOMIC512 0x00000002 +#define SQLITE_IOCAP_ATOMIC1K 0x00000004 +#define SQLITE_IOCAP_ATOMIC2K 0x00000008 +#define SQLITE_IOCAP_ATOMIC4K 0x00000010 +#define SQLITE_IOCAP_ATOMIC8K 0x00000020 +#define SQLITE_IOCAP_ATOMIC16K 0x00000040 +#define SQLITE_IOCAP_ATOMIC32K 0x00000080 +#define SQLITE_IOCAP_ATOMIC64K 0x00000100 +#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 +#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 +#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 +#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 /* ** CAPI3REF: File Locking Levels @@ -1031,6 +1145,18 @@ SQLITE_API int sqlite3_exec( ** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. ** If the lower four bits equal SQLITE_SYNC_FULL, that means ** to use Mac OS X style fullsync instead of fsync(). +** +** Do not confuse the SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags +** with the [PRAGMA synchronous]=NORMAL and [PRAGMA synchronous]=FULL +** settings. The [synchronous pragma] determines when calls to the +** xSync VFS method occur and applies uniformly across all platforms. +** The SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL flags determine how +** energetic or rigorous or forceful the sync operations are and +** only make a difference on Mac OSX for the default SQLite code. +** (Third-party VFS implementations might also make the distinction +** between SQLITE_SYNC_NORMAL and SQLITE_SYNC_FULL, but among the +** operating systems natively supported by SQLite, only Mac OSX +** cares about the difference.) */ #define SQLITE_SYNC_NORMAL 0x00002 #define SQLITE_SYNC_FULL 0x00003 @@ -1055,17 +1181,18 @@ struct sqlite3_file { /* ** CAPI3REF: OS Interface File Virtual Methods Object ** -** Every file opened by the [sqlite3_vfs] xOpen method populates an +** Every file opened by the [sqlite3_vfs.xOpen] method populates an ** [sqlite3_file] object (or, more commonly, a subclass of the ** [sqlite3_file] object) with a pointer to an instance of this object. ** This object defines the methods used to perform various operations ** against the open file represented by the [sqlite3_file] object. ** -** If the xOpen method sets the sqlite3_file.pMethods element +** If the [sqlite3_vfs.xOpen] method sets the sqlite3_file.pMethods element ** to a non-NULL pointer, then the sqlite3_io_methods.xClose method -** may be invoked even if the xOpen reported that it failed. The -** only way to prevent a call to xClose following a failed xOpen -** is for the xOpen to set the sqlite3_file.pMethods element to NULL. +** may be invoked even if the [sqlite3_vfs.xOpen] reported that it failed. The +** only way to prevent a call to xClose following a failed [sqlite3_vfs.xOpen] +** is for the [sqlite3_vfs.xOpen] to set the sqlite3_file.pMethods element +** to NULL. ** ** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or ** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). @@ -1099,7 +1226,9 @@ struct sqlite3_file { ** core reserves all opcodes less than 100 for its own use. ** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. ** Applications that define a custom xFileControl method should use opcodes -** greater than 100 to avoid conflicts. +** greater than 100 to avoid conflicts. VFS implementations should +** return [SQLITE_NOTFOUND] for file control opcodes that they do not +** recognize. ** ** The xSectorSize() method returns the sector size of the ** device that underlies the file. The sector size is the @@ -1154,6 +1283,12 @@ struct sqlite3_io_methods { int (*xFileControl)(sqlite3_file*, int op, void *pArg); int (*xSectorSize)(sqlite3_file*); int (*xDeviceCharacteristics)(sqlite3_file*); + /* Methods above are valid for version 1 */ + int (*xShmMap)(sqlite3_file*, int iPg, int pgsz, int, void volatile**); + int (*xShmLock)(sqlite3_file*, int offset, int n, int flags); + void (*xShmBarrier)(sqlite3_file*); + int (*xShmUnmap)(sqlite3_file*, int deleteFlag); + /* Methods above are valid for version 2 */ /* Additional methods may be added in future releases */ }; @@ -1171,11 +1306,161 @@ struct sqlite3_io_methods { ** into an integer that the pArg argument points to. This capability ** is used during testing and only needs to be supported when SQLITE_TEST ** is defined. +**
    +**
  • [[SQLITE_FCNTL_SIZE_HINT]] +** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS +** layer a hint of how large the database file will grow to be during the +** current transaction. This hint is not guaranteed to be accurate but it +** is often close. The underlying VFS might choose to preallocate database +** file space based on this hint in order to help writes to the database +** file run faster. +** +**
  • [[SQLITE_FCNTL_CHUNK_SIZE]] +** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS +** extends and truncates the database file in chunks of a size specified +** by the user. The fourth argument to [sqlite3_file_control()] should +** point to an integer (type int) containing the new chunk-size to use +** for the nominated database. Allocating database file space in large +** chunks (say 1MB at a time), may reduce file-system fragmentation and +** improve performance on some systems. +** +**
  • [[SQLITE_FCNTL_FILE_POINTER]] +** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer +** to the [sqlite3_file] object associated with a particular database +** connection. See the [sqlite3_file_control()] documentation for +** additional information. +** +**
  • [[SQLITE_FCNTL_SYNC_OMITTED]] +** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by +** SQLite and sent to all VFSes in place of a call to the xSync method +** when the database connection has [PRAGMA synchronous] set to OFF.)^ +** Some specialized VFSes need this signal in order to operate correctly +** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most +** VFSes do not need this signal and should silently ignore this opcode. +** Applications should not call [sqlite3_file_control()] with this +** opcode as doing so may disrupt the operation of the specialized VFSes +** that do require it. +** +**
  • [[SQLITE_FCNTL_WIN32_AV_RETRY]] +** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic +** retry counts and intervals for certain disk I/O operations for the +** windows [VFS] in order to provide robustness in the presence of +** anti-virus programs. By default, the windows VFS will retry file read, +** file write, and file delete operations up to 10 times, with a delay +** of 25 milliseconds before the first retry and with the delay increasing +** by an additional 25 milliseconds with each subsequent retry. This +** opcode allows these two values (10 retries and 25 milliseconds of delay) +** to be adjusted. The values are changed for all database connections +** within the same process. The argument is a pointer to an array of two +** integers where the first integer i the new retry count and the second +** integer is the delay. If either integer is negative, then the setting +** is not changed but instead the prior value of that setting is written +** into the array entry, allowing the current retry settings to be +** interrogated. The zDbName parameter is ignored. +** +**
  • [[SQLITE_FCNTL_PERSIST_WAL]] +** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the +** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary +** write ahead log and shared memory files used for transaction control +** are automatically deleted when the latest connection to the database +** closes. Setting persistent WAL mode causes those files to persist after +** close. Persisting the files is useful when other processes that do not +** have write permission on the directory containing the database file want +** to read the database file, as the WAL and shared memory files must exist +** in order for the database to be readable. The fourth parameter to +** [sqlite3_file_control()] for this opcode should be a pointer to an integer. +** That integer is 0 to disable persistent WAL mode or 1 to enable persistent +** WAL mode. If the integer is -1, then it is overwritten with the current +** WAL persistence setting. +** +**
  • [[SQLITE_FCNTL_POWERSAFE_OVERWRITE]] +** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the +** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting +** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the +** xDeviceCharacteristics methods. The fourth parameter to +** [sqlite3_file_control()] for this opcode should be a pointer to an integer. +** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage +** mode. If the integer is -1, then it is overwritten with the current +** zero-damage mode setting. +** +**
  • [[SQLITE_FCNTL_OVERWRITE]] +** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening +** a write transaction to indicate that, unless it is rolled back for some +** reason, the entire database file will be overwritten by the current +** transaction. This is used by VACUUM operations. +** +**
  • [[SQLITE_FCNTL_VFSNAME]] +** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of +** all [VFSes] in the VFS stack. The names are of all VFS shims and the +** final bottom-level VFS are written into memory obtained from +** [sqlite3_malloc()] and the result is stored in the char* variable +** that the fourth parameter of [sqlite3_file_control()] points to. +** The caller is responsible for freeing the memory when done. As with +** all file-control actions, there is no guarantee that this will actually +** do anything. Callers should initialize the char* variable to a NULL +** pointer in case this file-control is not implemented. This file-control +** is intended for diagnostic use only. +** +**
  • [[SQLITE_FCNTL_PRAGMA]] +** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA] +** file control is sent to the open [sqlite3_file] object corresponding +** to the database file to which the pragma statement refers. ^The argument +** to the [SQLITE_FCNTL_PRAGMA] file control is an array of +** pointers to strings (char**) in which the second element of the array +** is the name of the pragma and the third element is the argument to the +** pragma or NULL if the pragma has no argument. ^The handler for an +** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element +** of the char** argument point to a string obtained from [sqlite3_mprintf()] +** or the equivalent and that string will become the result of the pragma or +** the error message if the pragma fails. ^If the +** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal +** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA] +** file control returns [SQLITE_OK], then the parser assumes that the +** VFS has handled the PRAGMA itself and the parser generates a no-op +** prepared statement. ^If the [SQLITE_FCNTL_PRAGMA] file control returns +** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means +** that the VFS encountered an error while handling the [PRAGMA] and the +** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA] +** file control occurs at the beginning of pragma statement analysis and so +** it is able to override built-in [PRAGMA] statements. +** +**
  • [[SQLITE_FCNTL_BUSYHANDLER]] +** ^This file-control may be invoked by SQLite on the database file handle +** shortly after it is opened in order to provide a custom VFS with access +** to the connections busy-handler callback. The argument is of type (void **) +** - an array of two (void *) values. The first (void *) actually points +** to a function of type (int (*)(void *)). In order to invoke the connections +** busy-handler, this function should be invoked with the second (void *) in +** the array as the only argument. If it returns non-zero, then the operation +** should be retried. If it returns zero, the custom VFS should abandon the +** current operation. +** +**
  • [[SQLITE_FCNTL_TEMPFILENAME]] +** ^Application can invoke this file-control to have SQLite generate a +** temporary filename using the same algorithm that is followed to generate +** temporary filenames for TEMP tables and other internal uses. The +** argument should be a char** which will be filled with the filename +** written into memory obtained from [sqlite3_malloc()]. The caller should +** invoke [sqlite3_free()] on the result to avoid a memory leak. +** +**
*/ -#define SQLITE_FCNTL_LOCKSTATE 1 -#define SQLITE_GET_LOCKPROXYFILE 2 -#define SQLITE_SET_LOCKPROXYFILE 3 -#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_LOCKSTATE 1 +#define SQLITE_GET_LOCKPROXYFILE 2 +#define SQLITE_SET_LOCKPROXYFILE 3 +#define SQLITE_LAST_ERRNO 4 +#define SQLITE_FCNTL_SIZE_HINT 5 +#define SQLITE_FCNTL_CHUNK_SIZE 6 +#define SQLITE_FCNTL_FILE_POINTER 7 +#define SQLITE_FCNTL_SYNC_OMITTED 8 +#define SQLITE_FCNTL_WIN32_AV_RETRY 9 +#define SQLITE_FCNTL_PERSIST_WAL 10 +#define SQLITE_FCNTL_OVERWRITE 11 +#define SQLITE_FCNTL_VFSNAME 12 +#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13 +#define SQLITE_FCNTL_PRAGMA 14 +#define SQLITE_FCNTL_BUSYHANDLER 15 +#define SQLITE_FCNTL_TEMPFILENAME 16 /* ** CAPI3REF: Mutex Handle @@ -1194,7 +1479,8 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** ** An instance of the sqlite3_vfs object defines the interface between ** the SQLite core and the underlying operating system. The "vfs" -** in the name of the object stands for "virtual file system". +** in the name of the object stands for "virtual file system". See +** the [VFS | VFS documentation] for further information. ** ** The value of the iVersion field is initially 1 but may be larger in ** future versions of SQLite. Additional fields may be appended to this @@ -1223,15 +1509,20 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** -** SQLite will guarantee that the zFilename parameter to xOpen +** [[sqlite3_vfs.xOpen]] +** ^SQLite guarantees that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained -** from xFullPathname(). SQLite further guarantees that +** from xFullPathname() with an optional suffix added. +** ^If a suffix is added to the zFilename parameter, it will +** consist of a single "-" character followed by no more than +** 11 alphanumeric and/or "-" characters. +** ^SQLite further guarantees that ** the string will be valid and unchanged until xClose() is ** called. Because of the previous sentence, ** the [sqlite3_file] can safely store a pointer to the ** filename if it needs to remember the filename for some reason. -** If the zFilename parameter is xOpen is a NULL pointer then xOpen -** must invent its own temporary name for the file. Whenever the +** If the zFilename parameter to xOpen is a NULL pointer then xOpen +** must invent its own temporary name for the file. ^Whenever the ** xFilename parameter is NULL it will also be the case that the ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. ** @@ -1242,7 +1533,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** If xOpen() opens a file read-only then it sets *pOutFlags to ** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. ** -** SQLite will also add one of the following flags to the xOpen() +** ^(SQLite will also add one of the following flags to the xOpen() ** call, depending on the object being opened: ** **
    @@ -1253,7 +1544,8 @@ typedef struct sqlite3_mutex sqlite3_mutex; **
  • [SQLITE_OPEN_TRANSIENT_DB] **
  • [SQLITE_OPEN_SUBJOURNAL] **
  • [SQLITE_OPEN_MASTER_JOURNAL] -**
+**
  • [SQLITE_OPEN_WAL] +** )^ ** ** The file I/O implementation can use the object type flags to ** change the way it deals with files. For example, an application @@ -1272,10 +1564,11 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** ** ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be -** deleted when it is closed. The [SQLITE_OPEN_DELETEONCLOSE] -** will be set for TEMP databases, journals and for subjournals. +** deleted when it is closed. ^The [SQLITE_OPEN_DELETEONCLOSE] +** will be set for TEMP databases and their journals, transient +** databases, and subjournals. ** -** The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction +** ^The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction ** with the [SQLITE_OPEN_CREATE] flag, which are both directly ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the @@ -1284,7 +1577,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** It is not used to indicate the file should be opened ** for exclusive access. ** -** At least szOsFile bytes of memory are allocated by SQLite +** ^At least szOsFile bytes of memory are allocated by SQLite ** to hold the [sqlite3_file] structure passed as the third ** argument to xOpen. The xOpen method does not have to ** allocate the structure; it should just fill it in. Note that @@ -1294,33 +1587,54 @@ typedef struct sqlite3_mutex sqlite3_mutex; ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** -** The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] +** [[sqlite3_vfs.xAccess]] +** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] ** to test whether a file is at least readable. The file can be a ** directory. ** -** SQLite will always allocate at least mxPathname+1 bytes for the +** ^SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** -** The xRandomness(), xSleep(), and xCurrentTime() interfaces -** are not strictly a part of the filesystem, but they are +** The xRandomness(), xSleep(), xCurrentTime(), and xCurrentTimeInt64() +** interfaces are not strictly a part of the filesystem, but they are ** included in the VFS structure for completeness. ** The xRandomness() function attempts to return nBytes bytes ** of good-quality randomness into zOut. The return value is ** the actual number of bytes of randomness obtained. ** The xSleep() method causes the calling thread to sleep for at -** least the number of microseconds given. The xCurrentTime() -** method returns a Julian Day Number for the current date and time. +** least the number of microseconds given. ^The xCurrentTime() +** method returns a Julian Day Number for the current date and time as +** a floating point value. +** ^The xCurrentTimeInt64() method returns, as an integer, the Julian +** Day Number multiplied by 86400000 (the number of milliseconds in +** a 24-hour day). +** ^SQLite will use the xCurrentTimeInt64() method to get the current +** date and time if that method is available (if iVersion is 2 or +** greater and the function pointer is not NULL) and will fall back +** to xCurrentTime() if xCurrentTimeInt64() is unavailable. ** +** ^The xSetSystemCall(), xGetSystemCall(), and xNestSystemCall() interfaces +** are not used by the SQLite core. These optional interfaces are provided +** by some VFSes to facilitate testing of the VFS code. By overriding +** system calls with functions under its control, a test program can +** simulate faults and error conditions that would otherwise be difficult +** or impossible to induce. The set of system calls that can be overridden +** varies from one VFS to another, and from one version of the same VFS to the +** next. Applications that use these interfaces must be prepared for any +** or all of these interfaces to be NULL or for their behavior to change +** from one release to the next. Applications must not attempt to access +** any of these methods if the iVersion of the VFS is less than 3. */ typedef struct sqlite3_vfs sqlite3_vfs; +typedef void (*sqlite3_syscall_ptr)(void); struct sqlite3_vfs { - int iVersion; /* Structure version number */ + int iVersion; /* Structure version number (currently 3) */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ @@ -1339,8 +1653,23 @@ struct sqlite3_vfs { int (*xSleep)(sqlite3_vfs*, int microseconds); int (*xCurrentTime)(sqlite3_vfs*, double*); int (*xGetLastError)(sqlite3_vfs*, int, char *); - /* New fields may be appended in figure versions. The iVersion - ** value will increment whenever this happens. */ + /* + ** The methods above are in version 1 of the sqlite_vfs object + ** definition. Those that follow are added in version 2 or later + */ + int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); + /* + ** The methods above are in versions 1 and 2 of the sqlite_vfs object. + ** Those below are for version 3 and greater. + */ + int (*xSetSystemCall)(sqlite3_vfs*, const char *zName, sqlite3_syscall_ptr); + sqlite3_syscall_ptr (*xGetSystemCall)(sqlite3_vfs*, const char *zName); + const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName); + /* + ** The methods above are in versions 1 through 3 of the sqlite_vfs object. + ** New fields may be appended in figure versions. The iVersion + ** value will increment whenever this happens. + */ }; /* @@ -1352,13 +1681,58 @@ struct sqlite3_vfs { ** With SQLITE_ACCESS_EXISTS, the xAccess method ** simply checks whether the file exists. ** With SQLITE_ACCESS_READWRITE, the xAccess method -** checks whether the file is both readable and writable. +** checks whether the named directory is both readable and writable +** (in other words, if files can be added, removed, and renamed within +** the directory). +** The SQLITE_ACCESS_READWRITE constant is currently used only by the +** [temp_store_directory pragma], though this could change in a future +** release of SQLite. ** With SQLITE_ACCESS_READ, the xAccess method -** checks whether the file is readable. +** checks whether the file is readable. The SQLITE_ACCESS_READ constant is +** currently unused, though it might be used in a future release of +** SQLite. */ #define SQLITE_ACCESS_EXISTS 0 -#define SQLITE_ACCESS_READWRITE 1 -#define SQLITE_ACCESS_READ 2 +#define SQLITE_ACCESS_READWRITE 1 /* Used by PRAGMA temp_store_directory */ +#define SQLITE_ACCESS_READ 2 /* Unused */ + +/* +** CAPI3REF: Flags for the xShmLock VFS method +** +** These integer constants define the various locking operations +** allowed by the xShmLock method of [sqlite3_io_methods]. The +** following are the only legal combinations of flags to the +** xShmLock method: +** +**
      +**
    • SQLITE_SHM_LOCK | SQLITE_SHM_SHARED +**
    • SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE +**
    • SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED +**
    • SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE +**
    +** +** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as +** was given no the corresponding lock. +** +** The xShmLock method can transition between unlocked and SHARED or +** between unlocked and EXCLUSIVE. It cannot transition between SHARED +** and EXCLUSIVE. +*/ +#define SQLITE_SHM_UNLOCK 1 +#define SQLITE_SHM_LOCK 2 +#define SQLITE_SHM_SHARED 4 +#define SQLITE_SHM_EXCLUSIVE 8 + +/* +** CAPI3REF: Maximum xShmLock index +** +** The xShmLock method on [sqlite3_io_methods] may use values +** between 0 and this upper bound as its "offset" argument. +** The SQLite core will never attempt to acquire or release a +** lock outside of this range +*/ +#define SQLITE_SHM_NLOCK 8 + /* ** CAPI3REF: Initialize The SQLite Library @@ -1442,7 +1816,6 @@ SQLITE_API int sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library -** EXPERIMENTAL ** ** The sqlite3_config() interface is used to make global configuration ** changes to SQLite in order to tune SQLite to the specific needs of @@ -1461,44 +1834,37 @@ SQLITE_API int sqlite3_os_end(void); ** implementation of an application-defined [sqlite3_os_init()]. ** ** The first argument to sqlite3_config() is an integer -** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines +** [configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments -** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] +** vary depending on the [configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); +SQLITE_API int sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections -** EXPERIMENTAL ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single -** [database connection] (specified in the first argument). The -** sqlite3_db_config() interface should only be used immediately after -** the database connection is created using [sqlite3_open()], -** [sqlite3_open16()], or [sqlite3_open_v2()]. +** [database connection] (specified in the first argument). ** ** The second argument to sqlite3_db_config(D,V,...) is the -** configuration verb - an integer code that indicates what -** aspect of the [database connection] is being configured. -** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE]. -** New verbs are likely to be added in future releases of SQLite. -** Additional arguments depend on the verb. +** [SQLITE_DBCONFIG_LOOKASIDE | configuration verb] - an integer code +** that indicates what aspect of the [database connection] is being configured. +** Subsequent arguments vary depending on the configuration verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); +SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines -** EXPERIMENTAL ** ** An instance of this object defines the interface between SQLite ** and low-level memory allocation routines. @@ -1522,16 +1888,10 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); ** order to verify that SQLite recovers gracefully from such ** conditions. ** -** The xMalloc and xFree methods must work like the -** malloc() and free() functions from the standard C library. -** The xRealloc method must work like realloc() from the standard C library -** with the exception that if the second argument to xRealloc is zero, -** xRealloc must be a no-op - it must not perform any allocation or -** deallocation. ^SQLite guarantees that the second argument to +** The xMalloc, xRealloc, and xFree methods must work like the +** malloc(), realloc() and free() functions from the standard C library. +** ^SQLite guarantees that the second argument to ** xRealloc is always a value returned by a prior call to xRoundup. -** And so in cases where xRoundup always returns a positive number, -** xRealloc can perform exactly as the standard library realloc() and -** still be in compliance with this specification. ** ** xSize should return the allocated size of a memory allocation ** previously obtained from xMalloc or xRealloc. The allocated size @@ -1580,7 +1940,7 @@ struct sqlite3_mem_methods { /* ** CAPI3REF: Configuration Options -** EXPERIMENTAL +** KEYWORDS: {configuration option} ** ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. @@ -1593,7 +1953,7 @@ struct sqlite3_mem_methods { ** is invoked. ** **
    -**
    SQLITE_CONFIG_SINGLETHREAD
    +** [[SQLITE_CONFIG_SINGLETHREAD]]
    SQLITE_CONFIG_SINGLETHREAD
    **
    There are no arguments to this option. ^This option sets the ** [threading mode] to Single-thread. In other words, it disables ** all mutexing and puts SQLite into a mode where it can only be used @@ -1604,7 +1964,7 @@ struct sqlite3_mem_methods { ** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD ** configuration option.
    ** -**
    SQLITE_CONFIG_MULTITHREAD
    +** [[SQLITE_CONFIG_MULTITHREAD]]
    SQLITE_CONFIG_MULTITHREAD
    **
    There are no arguments to this option. ^This option sets the ** [threading mode] to Multi-thread. In other words, it disables ** mutexing on [database connection] and [prepared statement] objects. @@ -1618,7 +1978,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_MULTITHREAD configuration option.
    ** -**
    SQLITE_CONFIG_SERIALIZED
    +** [[SQLITE_CONFIG_SERIALIZED]]
    SQLITE_CONFIG_SERIALIZED
    **
    There are no arguments to this option. ^This option sets the ** [threading mode] to Serialized. In other words, this option enables ** all mutexes including the recursive @@ -1634,7 +1994,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_SERIALIZED configuration option.
    ** -**
    SQLITE_CONFIG_MALLOC
    +** [[SQLITE_CONFIG_MALLOC]]
    SQLITE_CONFIG_MALLOC
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The argument specifies ** alternative low-level memory allocation routines to be used in place of @@ -1642,7 +2002,7 @@ struct sqlite3_mem_methods { ** its own private copy of the content of the [sqlite3_mem_methods] structure ** before the [sqlite3_config()] call returns.
    ** -**
    SQLITE_CONFIG_GETMALLOC
    +** [[SQLITE_CONFIG_GETMALLOC]]
    SQLITE_CONFIG_GETMALLOC
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ @@ -1650,7 +2010,7 @@ struct sqlite3_mem_methods { ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example.
    ** -**
    SQLITE_CONFIG_MEMSTATUS
    +** [[SQLITE_CONFIG_MEMSTATUS]]
    SQLITE_CONFIG_MEMSTATUS
    **
    ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the @@ -1658,7 +2018,7 @@ struct sqlite3_mem_methods { **
      **
    • [sqlite3_memory_used()] **
    • [sqlite3_memory_highwater()] -**
    • [sqlite3_soft_heap_limit()] +**
    • [sqlite3_soft_heap_limit64()] **
    • [sqlite3_status()] **
    )^ ** ^Memory allocation statistics are enabled by default unless SQLite is @@ -1666,28 +2026,27 @@ struct sqlite3_mem_methods { ** allocation statistics are disabled by default. **
    ** -**
    SQLITE_CONFIG_SCRATCH
    +** [[SQLITE_CONFIG_SCRATCH]]
    SQLITE_CONFIG_SCRATCH
    **
    ^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scrach allocations will be +** aligned memory buffer from which the scratch allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz -** argument must be a multiple of 16. The sz parameter should be a few bytes -** larger than the actual scratch space required due to internal overhead. +** argument must be a multiple of 16. ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. -** ^SQLite will use no more than one scratch buffer per thread. So -** N should be set to the expected maximum number of threads. ^SQLite will -** never require a scratch buffer that is more than 6 times the database -** page size. ^If SQLite needs needs additional scratch memory beyond -** what is provided by this configuration option, then +** ^SQLite will use no more than two scratch buffers per thread. So +** N should be set to twice the expected maximum number of threads. +** ^SQLite will never require a scratch buffer that is more than 6 +** times the database page size. ^If SQLite needs needs additional +** scratch memory beyond what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.
    ** -**
    SQLITE_CONFIG_PAGECACHE
    +** [[SQLITE_CONFIG_PAGECACHE]]
    SQLITE_CONFIG_PAGECACHE
    **
    ^This option specifies a static memory buffer that SQLite can use for -** the database page cache with the default page cache implemenation. +** the database page cache with the default page cache implementation. ** This configuration should not be used if an application-define page -** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. +** cache implementation is loaded using the SQLITE_CONFIG_PCACHE2 option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page @@ -1700,12 +2059,11 @@ struct sqlite3_mem_methods { ** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. -** ^The implementation might use one or more of the N buffers to hold -** memory accounting information. The pointer in the first argument must +** The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.
    ** -**
    SQLITE_CONFIG_HEAP
    +** [[SQLITE_CONFIG_HEAP]]
    SQLITE_CONFIG_HEAP
    **
    ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. @@ -1718,9 +2076,11 @@ struct sqlite3_mem_methods { ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte -** boundary or subsequent behavior of SQLite will be undefined.
    +** boundary or subsequent behavior of SQLite will be undefined. +** The minimum allocation size is capped at 2**12. Reasonable values +** for the minimum allocation size are 2**5 through 2**8. ** -**
    SQLITE_CONFIG_MUTEX
    +** [[SQLITE_CONFIG_MUTEX]]
    SQLITE_CONFIG_MUTEX
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place @@ -1732,7 +2092,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will ** return [SQLITE_ERROR].
    ** -**
    SQLITE_CONFIG_GETMUTEX
    +** [[SQLITE_CONFIG_GETMUTEX]]
    SQLITE_CONFIG_GETMUTEX
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] @@ -1745,7 +2105,7 @@ struct sqlite3_mem_methods { ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will ** return [SQLITE_ERROR].
    ** -**
    SQLITE_CONFIG_LOOKASIDE
    +** [[SQLITE_CONFIG_LOOKASIDE]]
    SQLITE_CONFIG_LOOKASIDE
    **
    ^(This option takes two arguments that determine the default ** memory allocation for the lookaside memory allocator on each ** [database connection]. The first argument is the @@ -1755,17 +2115,80 @@ struct sqlite3_mem_methods { ** verb to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^
    ** -**
    SQLITE_CONFIG_PCACHE
    +** [[SQLITE_CONFIG_PCACHE2]]
    SQLITE_CONFIG_PCACHE2
    **
    ^(This option takes a single argument which is a pointer to -** an [sqlite3_pcache_methods] object. This object specifies the interface +** an [sqlite3_pcache_methods2] object. This object specifies the interface ** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.
    ** -**
    SQLITE_CONFIG_GETPCACHE
    +** [[SQLITE_CONFIG_GETPCACHE2]]
    SQLITE_CONFIG_GETPCACHE2
    **
    ^(This option takes a single argument which is a pointer to an -** [sqlite3_pcache_methods] object. SQLite copies of the current +** [sqlite3_pcache_methods2] object. SQLite copies of the current ** page cache implementation into that object.)^
    ** +** [[SQLITE_CONFIG_LOG]]
    SQLITE_CONFIG_LOG
    +**
    ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a +** function with a call signature of void(*)(void*,int,const char*), +** and a pointer to void. ^If the function pointer is not NULL, it is +** invoked by [sqlite3_log()] to process each logging event. ^If the +** function pointer is NULL, the [sqlite3_log()] interface becomes a no-op. +** ^The void pointer that is the second argument to SQLITE_CONFIG_LOG is +** passed through as the first parameter to the application-defined logger +** function whenever that function is invoked. ^The second parameter to +** the logger function is a copy of the first parameter to the corresponding +** [sqlite3_log()] call and is intended to be a [result code] or an +** [extended result code]. ^The third parameter passed to the logger is +** log message after formatting via [sqlite3_snprintf()]. +** The SQLite logging interface is not reentrant; the logger function +** supplied by the application must not invoke any SQLite interface. +** In a multi-threaded application, the application-defined logger +** function must be threadsafe.
    +** +** [[SQLITE_CONFIG_URI]]
    SQLITE_CONFIG_URI +**
    This option takes a single argument of type int. If non-zero, then +** URI handling is globally enabled. If the parameter is zero, then URI handling +** is globally disabled. If URI handling is globally enabled, all filenames +** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or +** specified as part of [ATTACH] commands are interpreted as URIs, regardless +** of whether or not the [SQLITE_OPEN_URI] flag is set when the database +** connection is opened. If it is globally disabled, filenames are +** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the +** database connection is opened. By default, URI handling is globally +** disabled. The default value may be changed by compiling with the +** [SQLITE_USE_URI] symbol defined. +** +** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]]
    SQLITE_CONFIG_COVERING_INDEX_SCAN +**
    This option takes a single integer argument which is interpreted as +** a boolean in order to enable or disable the use of covering indices for +** full table scans in the query optimizer. The default setting is determined +** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on" +** if that compile-time option is omitted. +** The ability to disable the use of covering indices for full table scans +** is because some incorrectly coded legacy applications might malfunction +** malfunction when the optimization is enabled. Providing the ability to +** disable the optimization allows the older, buggy application code to work +** without change even with newer versions of SQLite. +** +** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]] +**
    SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE +**
    These options are obsolete and should not be used by new code. +** They are retained for backwards compatibility but are now no-ops. +**
    +** +** [[SQLITE_CONFIG_SQLLOG]] +**
    SQLITE_CONFIG_SQLLOG +**
    This option is only available if sqlite is compiled with the +** SQLITE_ENABLE_SQLLOG pre-processor macro defined. The first argument should +** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int). +** The second should be of type (void*). The callback is invoked by the library +** in three separate circumstances, identified by the value passed as the +** fourth parameter. If the fourth parameter is 0, then the database connection +** passed as the second argument has just been opened. The third argument +** points to a buffer containing the name of the main database file. If the +** fourth parameter is 1, then the SQL statement that the third parameter +** points to has just been executed. Or, if the fourth parameter is 2, then +** the connection being passed as the second parameter is being closed. The +** third parameter is passed NULL In this case. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -1781,12 +2204,17 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ -#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ -#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ +#define SQLITE_CONFIG_PCACHE 14 /* no-op */ +#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */ +#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ +#define SQLITE_CONFIG_URI 17 /* int */ +#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */ +#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */ +#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */ /* -** CAPI3REF: Configuration Options -** EXPERIMENTAL +** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. @@ -1803,7 +2231,7 @@ struct sqlite3_mem_methods { **
    ^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an memory buffer to use for lookaside memory. +** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the @@ -1812,12 +2240,40 @@ struct sqlite3_mem_methods { ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally -** rounded down to the next smaller -** multiple of 8. See also: [SQLITE_CONFIG_LOOKASIDE]
    +** rounded down to the next smaller multiple of 8. ^(The lookaside memory +** configuration for a database connection can only be changed when that +** connection is not currently using lookaside memory, or in other words +** when the "current value" returned by +** [sqlite3_db_status](D,[SQLITE_CONFIG_LOOKASIDE],...) is zero. +** Any attempt to change the lookaside memory configuration when lookaside +** memory is in use leaves the configuration unchanged and returns +** [SQLITE_BUSY].)^ +** +**
    SQLITE_DBCONFIG_ENABLE_FKEY
    +**
    ^This option is used to enable or disable the enforcement of +** [foreign key constraints]. There should be two additional arguments. +** The first argument is an integer which is 0 to disable FK enforcement, +** positive to enable FK enforcement or negative to leave FK enforcement +** unchanged. The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether FK enforcement is off or on +** following this call. The second parameter may be a NULL pointer, in +** which case the FK enforcement setting is not reported back.
    +** +**
    SQLITE_DBCONFIG_ENABLE_TRIGGER
    +**
    ^This option is used to enable or disable [CREATE TRIGGER | triggers]. +** There should be two additional arguments. +** The first argument is an integer which is 0 to disable triggers, +** positive to enable triggers or negative to leave the setting unchanged. +** The second parameter is a pointer to an integer into which +** is written 0 or 1 to indicate whether triggers are disabled or enabled +** following this call. The second parameter may be a NULL pointer, in +** which case the trigger setting is not reported back.
    ** ** */ -#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ +#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ +#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ /* @@ -1841,13 +2297,17 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); ** ** ^This routine returns the [rowid] of the most recent ** successful [INSERT] into the database from the [database connection] -** in the first argument. ^If no successful [INSERT]s +** in the first argument. ^As of SQLite version 3.7.7, this routines +** records the last insert rowid of both ordinary tables and [virtual tables]. +** ^If no successful [INSERT]s ** have ever occurred on that database connection, zero is returned. ** -** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted -** row is returned by this routine as long as the trigger is running. -** But once the trigger terminates, the value returned by this routine -** reverts to the last value inserted before the trigger fired.)^ +** ^(If an [INSERT] occurs within a trigger or within a [virtual table] +** method, then this routine will return the [rowid] of the inserted +** row as long as the trigger or virtual table method is running. +** But once the trigger or virtual table method ends, the value returned +** by this routine reverts to what it was before the trigger or virtual +** table method began.)^ ** ** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this @@ -2118,6 +2578,9 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** +** This is a legacy interface that is preserved for backwards compatibility. +** Use of this interface is not recommended. +** ** Definition: A result table is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. @@ -2138,7 +2601,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** -** As an example of the result table format, suppose a query result +** ^(As an example of the result table format, suppose a query result ** is as follows: ** **
    @@ -2162,7 +2625,7 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
     **        azResult[5] = "28";
     **        azResult[6] = "Cindy";
     **        azResult[7] = "21";
    -** 
    +** )^ ** ** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 @@ -2170,19 +2633,19 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); ** pointer given in its 3rd parameter. ** ** After the application has finished with the result from sqlite3_get_table(), -** it should pass the result table pointer to sqlite3_free_table() in order to +** it must pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** -** ^(The sqlite3_get_table() interface is implemented as a wrapper around +** The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or -** [sqlite3_errmsg()].)^ +** [sqlite3_errmsg()]. */ SQLITE_API int sqlite3_get_table( sqlite3 *db, /* An open database */ @@ -2207,7 +2670,7 @@ SQLITE_API void sqlite3_free_table(char **result); ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. ** -** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from +** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from ** the standard C library. The result is written into the ** buffer supplied as the second parameter whose size is given by ** the first parameter. Note that the order of the @@ -2226,12 +2689,14 @@ SQLITE_API void sqlite3_free_table(char **result); ** the zero terminator. So the longest string that can be completely ** written will be n-1 characters. ** +** ^The sqlite3_vsnprintf() routine is a varargs version of sqlite3_snprintf(). +** ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", and "%z" options. ** -** ^(The %q option works like %s in that it substitutes a null-terminated +** ^(The %q option works like %s in that it substitutes a nul-terminated ** string from the argument list. But %q also doubles every '\'' character. ** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into @@ -2289,6 +2754,7 @@ SQLITE_API void sqlite3_free_table(char **result); SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); +SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list); /* ** CAPI3REF: Memory Allocation Subsystem @@ -2334,19 +2800,21 @@ SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() -** is always aligned to at least an 8 byte boundary. +** is always aligned to at least an 8 byte boundary, or to a +** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time +** option is used. ** ** In SQLite version 3.5.0 and 3.5.1, it was possible to define ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in ** implementation of these routines to be omitted. That capability ** is no longer provided. Only built-in memory allocators can be used. ** -** The Windows OS interface layer calls +** Prior to SQLite version 3.7.10, the Windows OS interface layer called ** the system malloc() and free() directly when converting ** filenames between the UTF-8 encoding used by SQLite ** and whatever filename encoding is used by the particular Windows -** installation. Memory allocation errors are detected, but -** they are reported back as [SQLITE_CANTOPEN] or +** installation. Memory allocation errors were detected, but +** they were reported back as [SQLITE_CANTOPEN] or ** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. ** ** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] @@ -2411,7 +2879,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** -** ^This routine registers a authorizer callback with a particular +** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], @@ -2502,6 +2970,9 @@ SQLITE_API int sqlite3_set_authorizer( ** to signal SQLite whether or not the action is permitted. See the ** [sqlite3_set_authorizer | authorizer documentation] for additional ** information. +** +** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code] +** from the [sqlite3_vtab_on_conflict()] interface. */ #define SQLITE_DENY 1 /* Abort the SQL statement with an error */ #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ @@ -2562,7 +3033,6 @@ SQLITE_API int sqlite3_set_authorizer( /* ** CAPI3REF: Tracing And Profiling Functions -** EXPERIMENTAL ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. @@ -2578,26 +3048,43 @@ SQLITE_API int sqlite3_set_authorizer( ** ^The callback function registered by sqlite3_profile() is invoked ** as each SQL statement finishes. ^The profile callback contains ** the original statement text and an estimate of wall-clock time -** of how long that statement took to run. +** of how long that statement took to run. ^The profile callback +** time is in units of nanoseconds, however the current implementation +** is only capable of millisecond resolution so the six least significant +** digits in the time are meaningless. Future versions of SQLite +** might provide greater resolution on the profiler callback. The +** sqlite3_profile() function is considered experimental and is +** subject to change in future versions of SQLite. */ -SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); +SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** -** ^This routine configures a callback function - the -** progress callback - that is invoked periodically during long -** running calls to [sqlite3_exec()], [sqlite3_step()] and -** [sqlite3_get_table()]. An example use for this +** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback +** function X to be invoked periodically during long running calls to +** [sqlite3_exec()], [sqlite3_step()] and [sqlite3_get_table()] for +** database connection D. An example use for this ** interface is to keep a GUI updated during a large query. ** +** ^The parameter P is passed through as the only parameter to the +** callback function X. ^The parameter N is the number of +** [virtual machine instructions] that are evaluated between successive +** invocations of the callback X. +** +** ^Only a single progress handler may be defined at one time per +** [database connection]; setting a new progress handler cancels the +** old one. ^Setting parameter X to NULL disables the progress handler. +** ^The progress handler is also disabled by setting N to a value less +** than 1. +** ** ^If the progress callback returns non-zero, the operation is ** interrupted. This feature can be used to implement a ** "Cancel" button on a GUI progress dialog box. ** -** The progress handler must not do anything that will modify +** The progress handler callback must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. @@ -2608,7 +3095,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection ** -** ^These routines open an SQLite database file whose name is given by the +** ^These routines open an SQLite database file as specified by the ** filename argument. ^The filename argument is interpreted as UTF-8 for ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte ** order for sqlite3_open16(). ^(A [database connection] handle is usually @@ -2635,7 +3122,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** sqlite3_open_v2() can take one of ** the following three values, optionally combined with the ** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], -** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^ +** [SQLITE_OPEN_PRIVATECACHE], and/or [SQLITE_OPEN_URI] flags:)^ ** **
    ** ^(
    [SQLITE_OPEN_READONLY]
    @@ -2648,15 +3135,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** case the database must already exist, otherwise an error is returned.)^ ** ** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    -**
    The database is opened for reading and writing, and is creates it if +**
    The database is opened for reading and writing, and is created if ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().
    )^ **
    ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the -** combinations shown above or one of the combinations shown above combined -** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], -** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_SHAREDCACHE] flags, +** combinations shown above optionally combined with other +** [SQLITE_OPEN_READONLY | SQLITE_OPEN_* bits] ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection @@ -2671,6 +3157,11 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not ** participate in [shared cache mode] even if it is enabled. ** +** ^The fourth parameter to sqlite3_open_v2() is the name of the +** [sqlite3_vfs] object that defines the operating system interface that +** the new database connection should use. ^If the fourth parameter is +** a NULL pointer then the default [sqlite3_vfs] object is used. +** ** ^If the filename is ":memory:", then a private, temporary in-memory database ** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might @@ -2683,16 +3174,125 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); ** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** -** ^The fourth parameter to sqlite3_open_v2() is the name of the -** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. ^If the fourth parameter is -** a NULL pointer then the default [sqlite3_vfs] object is used. +** [[URI filenames in sqlite3_open()]]

    URI Filenames

    +** +** ^If [URI filename] interpretation is enabled, and the filename argument +** begins with "file:", then the filename is interpreted as a URI. ^URI +** filename interpretation is enabled if the [SQLITE_OPEN_URI] flag is +** set in the fourth argument to sqlite3_open_v2(), or if it has +** been enabled globally using the [SQLITE_CONFIG_URI] option with the +** [sqlite3_config()] method or by the [SQLITE_USE_URI] compile-time option. +** As of SQLite version 3.7.7, URI filename interpretation is turned off +** by default, but future releases of SQLite might enable URI filename +** interpretation by default. See "[URI filenames]" for additional +** information. +** +** URI filenames are parsed according to RFC 3986. ^If the URI contains an +** authority, then it must be either an empty string or the string +** "localhost". ^If the authority is not an empty string or "localhost", an +** error is returned to the caller. ^The fragment component of a URI, if +** present, is ignored. +** +** ^SQLite uses the path component of the URI as the name of the disk file +** which contains the database. ^If the path begins with a '/' character, +** then it is interpreted as an absolute path. ^If the path does not begin +** with a '/' (meaning that the authority section is omitted from the URI) +** then the path is interpreted as a relative path. +** ^On windows, the first component of an absolute path +** is a drive specification (e.g. "C:"). +** +** [[core URI query parameters]] +** The query component of a URI may contain parameters that are interpreted +** either by SQLite itself, or by a [VFS | custom VFS implementation]. +** SQLite interprets the following three query parameters: +** +**
      +**
    • vfs: ^The "vfs" parameter may be used to specify the name of +** a VFS object that provides the operating system interface that should +** be used to access the database file on disk. ^If this option is set to +** an empty string the default VFS object is used. ^Specifying an unknown +** VFS is an error. ^If sqlite3_open_v2() is used and the vfs option is +** present, then the VFS specified by the option takes precedence over +** the value passed as the fourth parameter to sqlite3_open_v2(). +** +**
    • mode: ^(The mode parameter may be set to either "ro", "rw", +** "rwc", or "memory". Attempting to set it to any other value is +** an error)^. +** ^If "ro" is specified, then the database is opened for read-only +** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the +** third argument to sqlite3_open_v2(). ^If the mode option is set to +** "rw", then the database is opened for read-write (but not create) +** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had +** been set. ^Value "rwc" is equivalent to setting both +** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is +** set to "memory" then a pure [in-memory database] that never reads +** or writes from disk is used. ^It is an error to specify a value for +** the mode parameter that is less restrictive than that specified by +** the flags passed in the third parameter to sqlite3_open_v2(). +** +**
    • cache: ^The cache parameter may be set to either "shared" or +** "private". ^Setting it to "shared" is equivalent to setting the +** SQLITE_OPEN_SHAREDCACHE bit in the flags argument passed to +** sqlite3_open_v2(). ^Setting the cache parameter to "private" is +** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit. +** ^If sqlite3_open_v2() is used and the "cache" parameter is present in +** a URI filename, its value overrides any behaviour requested by setting +** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag. +**
    +** +** ^Specifying an unknown parameter in the query component of a URI is not an +** error. Future versions of SQLite might understand additional query +** parameters. See "[query parameters with special meaning to SQLite]" for +** additional information. +** +** [[URI filename examples]]

    URI filename examples

    +** +** +**
    URI filenames Results +**
    file:data.db +** Open the file "data.db" in the current directory. +**
    file:/home/fred/data.db
    +** file:///home/fred/data.db
    +** file://localhost/home/fred/data.db
    +** Open the database file "/home/fred/data.db". +**
    file://darkstar/home/fred/data.db +** An error. "darkstar" is not a recognized authority. +**
    +** file:///C:/Documents%20and%20Settings/fred/Desktop/data.db +** Windows only: Open the file "data.db" on fred's desktop on drive +** C:. Note that the %20 escaping in this example is not strictly +** necessary - space characters can be used literally +** in URI filenames. +**
    file:data.db?mode=ro&cache=private +** Open file "data.db" in the current directory for read-only access. +** Regardless of whether or not shared-cache mode is enabled by +** default, use a private cache. +**
    file:/home/fred/data.db?vfs=unix-nolock +** Open file "/home/fred/data.db". Use the special VFS "unix-nolock". +**
    file:data.db?mode=readonly +** An error. "readonly" is not a valid option for the "mode" parameter. +**
    +** +** ^URI hexadecimal escape sequences (%HH) are supported within the path and +** query components of a URI. A hexadecimal escape sequence consists of a +** percent sign - "%" - followed by exactly two hexadecimal digits +** specifying an octet value. ^Before the path or query components of a +** URI filename are interpreted, they are encoded using UTF-8 and all +** hexadecimal escape sequences replaced by a single byte containing the +** corresponding octet. If this process generates an invalid UTF-8 encoding, +** the results are undefined. ** ** Note to Windows users: The encoding used for the filename argument ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever ** codepage is currently defined. Filenames containing international ** characters must be converted to UTF-8 prior to passing them into ** sqlite3_open() or sqlite3_open_v2(). +** +** Note to Windows Runtime users: The temporary directory must be set +** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various +** features that require the use of temporary files may fail. +** +** See also: [sqlite3_temp_directory] */ SQLITE_API int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ @@ -2709,6 +3309,50 @@ SQLITE_API int sqlite3_open_v2( const char *zVfs /* Name of VFS module to use */ ); +/* +** CAPI3REF: Obtain Values For URI Parameters +** +** These are utility routines, useful to VFS implementations, that check +** to see if a database file was a URI that contained a specific query +** parameter, and if so obtains the value of that query parameter. +** +** If F is the database filename pointer passed into the xOpen() method of +** a VFS implementation when the flags parameter to xOpen() has one or +** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and +** P is the name of the query parameter, then +** sqlite3_uri_parameter(F,P) returns the value of the P +** parameter if it exists or a NULL pointer if P does not appear as a +** query parameter on F. If P is a query parameter of F +** has no explicit value, then sqlite3_uri_parameter(F,P) returns +** a pointer to an empty string. +** +** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean +** parameter and returns true (1) or false (0) according to the value +** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the +** value of query parameter P is one of "yes", "true", or "on" in any +** case or if the value begins with a non-zero number. The +** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of +** query parameter P is one of "no", "false", or "off" in any case or +** if the value begins with a numeric zero. If P is not a query +** parameter on F or if the value of P is does not match any of the +** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0). +** +** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a +** 64-bit signed integer and returns that integer, or D if P does not +** exist. If the value of P is something other than an integer, then +** zero is returned. +** +** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and +** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and +** is not a database file pathname pointer that SQLite passed into the xOpen +** VFS method, then the behavior of this routine is undefined and probably +** undesirable. +*/ +SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); +SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); +SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); + + /* ** CAPI3REF: Error Codes And Messages ** @@ -2728,6 +3372,11 @@ SQLITE_API int sqlite3_open_v2( ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** +** ^The sqlite3_errstr() interface returns the English-language text +** that describes the [result code], as UTF-8. +** ^(Memory to hold the error message string is managed internally +** and must not be freed by the application)^. +** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. @@ -2746,6 +3395,7 @@ SQLITE_API int sqlite3_errcode(sqlite3 *db); SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); SQLITE_API const char *sqlite3_errmsg(sqlite3*); SQLITE_API const void *sqlite3_errmsg16(sqlite3*); +SQLITE_API const char *sqlite3_errstr(int); /* ** CAPI3REF: SQL Statement Object @@ -2781,17 +3431,22 @@ typedef struct sqlite3_stmt sqlite3_stmt; ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the -** new limit for that construct. The function returns the old limit.)^ +** new limit for that construct.)^ ** ** ^If the new limit is a negative number, the limit is unchanged. -** ^(For the limit category of SQLITE_LIMIT_XYZ there is a +** ^(For each limit category SQLITE_LIMIT_NAME there is a ** [limits | hard upper bound] -** set by a compile-time C preprocessor macro named -** [limits | SQLITE_MAX_XYZ]. +** set at compile-time by a C preprocessor macro called +** [limits | SQLITE_MAX_NAME]. ** (The "_LIMIT_" in the name is changed to "_MAX_".))^ ** ^Attempts to increase a limit above its hard upper bound are ** silently truncated to the hard upper bound. ** +** ^Regardless of whether or not the limit was changed, the +** [sqlite3_limit()] interface returns the prior value of the limit. +** ^Hence, to find the current value of a limit without changing it, +** simply invoke this interface with the third parameter set to -1. +** ** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a @@ -2819,42 +3474,45 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** Additional information is available at [limits | Limits in SQLite]. ** **
    -** ^(
    SQLITE_LIMIT_LENGTH
    -**
    The maximum size of any string or BLOB or table row.
    )^ +** [[SQLITE_LIMIT_LENGTH]] ^(
    SQLITE_LIMIT_LENGTH
    +**
    The maximum size of any string or BLOB or table row, in bytes.
    )^ ** -** ^(
    SQLITE_LIMIT_SQL_LENGTH
    +** [[SQLITE_LIMIT_SQL_LENGTH]] ^(
    SQLITE_LIMIT_SQL_LENGTH
    **
    The maximum length of an SQL statement, in bytes.
    )^ ** -** ^(
    SQLITE_LIMIT_COLUMN
    +** [[SQLITE_LIMIT_COLUMN]] ^(
    SQLITE_LIMIT_COLUMN
    **
    The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.
    )^ ** -** ^(
    SQLITE_LIMIT_EXPR_DEPTH
    +** [[SQLITE_LIMIT_EXPR_DEPTH]] ^(
    SQLITE_LIMIT_EXPR_DEPTH
    **
    The maximum depth of the parse tree on any expression.
    )^ ** -** ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    +** [[SQLITE_LIMIT_COMPOUND_SELECT]] ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    **
    The maximum number of terms in a compound SELECT statement.
    )^ ** -** ^(
    SQLITE_LIMIT_VDBE_OP
    +** [[SQLITE_LIMIT_VDBE_OP]] ^(
    SQLITE_LIMIT_VDBE_OP
    **
    The maximum number of instructions in a virtual machine program -** used to implement an SQL statement.
    )^ +** used to implement an SQL statement. This limit is not currently +** enforced, though that might be added in some future release of +** SQLite.)^ ** -** ^(
    SQLITE_LIMIT_FUNCTION_ARG
    +** [[SQLITE_LIMIT_FUNCTION_ARG]] ^(
    SQLITE_LIMIT_FUNCTION_ARG
    **
    The maximum number of arguments on a function.
    )^ ** -** ^(
    SQLITE_LIMIT_ATTACHED
    +** [[SQLITE_LIMIT_ATTACHED]] ^(
    SQLITE_LIMIT_ATTACHED
    **
    The maximum number of [ATTACH | attached databases].)^
    ** +** [[SQLITE_LIMIT_LIKE_PATTERN_LENGTH]] ** ^(
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH
    **
    The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.
    )^ ** +** [[SQLITE_LIMIT_VARIABLE_NUMBER]] ** ^(
    SQLITE_LIMIT_VARIABLE_NUMBER
    -**
    The maximum number of variables in an SQL statement that can -** be bound.
    )^ +**
    The maximum index number of any [parameter] in an SQL statement.)^ ** -** ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    +** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    **
    The maximum depth of recursion for triggers.
    )^ **
    */ @@ -2894,7 +3552,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** that the supplied string is nul-terminated, then there is a small ** performance advantage to be gained by passing an nByte parameter that ** is equal to the number of bytes in the input string including -** the nul-terminator bytes. +** the nul-terminator bytes as this saves SQLite from having to +** make a copy of the input string. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only @@ -2924,12 +3583,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); **
  • ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. ^If the schema has changed in -** a way that makes the statement no longer valid, [sqlite3_step()] will still -** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is -** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the -** error go away. Note: use [sqlite3_errmsg()] to find the text -** of the parsing error that results in an [SQLITE_SCHEMA] return. +** statement and try to run it again. **
  • ** **
  • @@ -2942,11 +3596,16 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); **
  • ** **
  • -** ^If the value of a [parameter | host parameter] in the WHERE clause might -** change the query plan for a statement, then the statement may be -** automatically recompiled (as if there had been a schema change) on the first -** [sqlite3_step()] call following any change to the -** [sqlite3_bind_text | bindings] of the [parameter]. +** ^If the specific value bound to [parameter | host parameter] in the +** WHERE clause might influence the choice of query plan for a statement, +** then the statement will be automatically recompiled, as if there had been +** a schema change, on the first [sqlite3_step()] call following any change +** to the [sqlite3_bind_text | bindings] of that [parameter]. +** ^The specific value of WHERE-clause [parameter] might influence the +** choice of query plan if the parameter is the left-hand side of a [LIKE] +** or [GLOB] operator or if the parameter is compared to an indexed column +** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. +** the **
  • ** */ @@ -2988,6 +3647,56 @@ SQLITE_API int sqlite3_prepare16_v2( */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); +/* +** CAPI3REF: Determine If An SQL Statement Writes The Database +** +** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if +** and only if the [prepared statement] X makes no direct changes to +** the content of the database file. +** +** Note that [application-defined SQL functions] or +** [virtual tables] might change the database indirectly as a side effect. +** ^(For example, if an application defines a function "eval()" that +** calls [sqlite3_exec()], then the following SQL statement would +** change the database file through side-effects: +** +**
    +**    SELECT eval('DELETE FROM t1') FROM t2;
    +** 
    +** +** But because the [SELECT] statement does not change the database file +** directly, sqlite3_stmt_readonly() would still return true.)^ +** +** ^Transaction control statements such as [BEGIN], [COMMIT], [ROLLBACK], +** [SAVEPOINT], and [RELEASE] cause sqlite3_stmt_readonly() to return true, +** since the statements themselves do not actually modify the database but +** rather they control the timing of when other statements modify the +** database. ^The [ATTACH] and [DETACH] statements also cause +** sqlite3_stmt_readonly() to return true since, while those statements +** change the configuration of a database connection, they do not make +** changes to the content of the database files on disk. +*/ +SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); + +/* +** CAPI3REF: Determine If A Prepared Statement Has Been Reset +** +** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the +** [prepared statement] S has been stepped at least once using +** [sqlite3_step(S)] but has not run to completion and/or has not +** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S) +** interface returns false if S is a NULL pointer. If S is not a +** NULL pointer and is not a pointer to a valid [prepared statement] +** object, then the behavior is undefined and probably undesirable. +** +** This interface can be used in combination [sqlite3_next_stmt()] +** to locate all prepared statements associated with a database +** connection that are in need of being reset. This can be used, +** for example, in diagnostic routines to search for prepared +** statements that are holding a transaction open. +*/ +SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*); + /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} @@ -3004,7 +3713,7 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** whether or not it requires a protected sqlite3_value. ** ** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. A internal mutex is held for a protected +** a mutex is held. An internal mutex is held for a protected ** sqlite3_value object but no mutex is held for an unprotected ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) @@ -3013,7 +3722,7 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications -** still make the distinction between between protected and unprotected +** still make the distinction between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the @@ -3059,7 +3768,7 @@ typedef struct sqlite3_context sqlite3_context; ** ** ** In the templates above, NNN represents an integer literal, -** and VVV represents an alphanumeric identifer.)^ ^The values of these +** and VVV represents an alphanumeric identifier.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** @@ -3082,12 +3791,25 @@ typedef struct sqlite3_context sqlite3_context; ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the ** number of bytes in the value, not the number of characters.)^ -** ^If the fourth parameter is negative, the length of the string is +** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16() +** is negative, then the length of the string is ** the number of bytes up to the first zero terminator. +** If the fourth parameter to sqlite3_bind_blob() is negative, then +** the behavior is undefined. +** If a non-negative fourth parameter is provided to sqlite3_bind_text() +** or sqlite3_bind_text16() then that parameter must be the byte offset +** where the NUL terminator would occur assuming the string were NUL +** terminated. If any NUL characters occur at byte offsets less than +** the value of the fourth parameter then the resulting string value will +** contain embedded NULs. The result of expressions involving strings +** with embedded NULs is undefined. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. ^If the fifth argument is +** string after SQLite has finished with it. ^The destructor is called +** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(), +** sqlite3_bind_text(), or sqlite3_bind_text16() fails. +** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then @@ -3208,6 +3930,8 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). +** +** See also: [sqlite3_data_count()] */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); @@ -3223,7 +3947,9 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] -** is destroyed by [sqlite3_finalize()] or until the next call to +** is destroyed by [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** ** ^If sqlite3_malloc() fails during the processing of either routine @@ -3249,7 +3975,9 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed -** using [sqlite3_finalize()] or until the same information is requested +** using [sqlite3_finalize()] or until the statement is automatically +** reprepared by the first call to [sqlite3_step()] for a particular run +** or until the same information is requested ** again in a different encoding. ** ** ^The names returned are the original un-aliased names of the @@ -3343,7 +4071,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** ^[SQLITE_BUSY] means that the database engine was unable to acquire the ** database locks it needs to do its job. ^If the statement is a [COMMIT] ** or occurs outside of an explicit transaction, then you can retry the -** statement. If the statement is not a [COMMIT] and occurs within a +** statement. If the statement is not a [COMMIT] and occurs within an ** explicit transaction then you should rollback the transaction before ** continuing. ** @@ -3373,6 +4101,18 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); ** be the case that the same database connection is being used by two or ** more threads at the same moment in time. ** +** For all versions of SQLite up to and including 3.6.23.1, a call to +** [sqlite3_reset()] was required after sqlite3_step() returned anything +** other than [SQLITE_ROW] before any subsequent invocation of +** sqlite3_step(). Failure to reset the prepared statement using +** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from +** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began +** calling [sqlite3_reset()] automatically in this circumstance rather +** than returning [SQLITE_MISUSE]. This is not considered a compatibility +** break because any application that ever receives an SQLITE_MISUSE error +** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option +** can be used to restore the legacy behavior. +** ** Goofy Interface Alert: In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call @@ -3390,8 +4130,20 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** -** ^The sqlite3_data_count(P) the number of columns in the -** of the result set of [prepared statement] P. +** ^The sqlite3_data_count(P) interface returns the number of columns in the +** current row of the result set of [prepared statement] P. +** ^If prepared statement P does not have results ready to return +** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of +** interfaces) then sqlite3_data_count(P) returns 0. +** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer. +** ^The sqlite3_data_count(P) routine returns 0 if the previous call to +** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P) +** will return non-zero if previous call to [sqlite3_step](P) returned +** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum] +** where it always returns zero since each step of that multi-step +** pragma returns 0 columns of data. +** +** See also: [sqlite3_column_count()] */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); @@ -3471,18 +4223,26 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. -** ^The value returned does not include the zero terminator at the end -** of the string. ^For clarity: the value returned is the number of +** ^If the result is NULL, then sqlite3_column_bytes() returns zero. +** +** ^If the result is a BLOB or UTF-16 string then the sqlite3_column_bytes16() +** routine returns the number of bytes in that BLOB or string. +** ^If the result is a UTF-8 string, then sqlite3_column_bytes16() converts +** the string to UTF-16 and then returns the number of bytes. +** ^If the result is a numeric value then sqlite3_column_bytes16() uses +** [sqlite3_snprintf()] to convert that value to a UTF-16 string and returns +** the number of bytes in that string. +** ^If the result is NULL, then sqlite3_column_bytes16() returns zero. +** +** ^The values returned by [sqlite3_column_bytes()] and +** [sqlite3_column_bytes16()] do not include the zero terminators at the end +** of the string. ^For clarity: the values returned by +** [sqlite3_column_bytes()] and [sqlite3_column_bytes16()] are the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero terminated. ^The return -** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary -** pointer, possibly even a NULL pointer. -** -** ^The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() -** but leaves the result in UTF-16 in native byte order instead of UTF-8. -** ^The zero terminator is not included in this count. +** even empty strings, are always zero-terminated. ^The return +** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object @@ -3527,10 +4287,10 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); ** used in the table for brevity and because they are familiar to most ** C programmers. ** -** ^Note that when type conversions occur, pointers returned by prior +** Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. -** ^(Type conversions and pointer invalidations might occur +** Type conversions and pointer invalidations might occur ** in the following cases: ** **
      @@ -3543,22 +4303,22 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); **
    • The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.
    • -**
    )^ +** ** ** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer -** that the prior pointer points to will have been modified. Other kinds +** that the prior pointer references will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** -** ^(The safest and easiest to remember policy is to invoke these routines +** The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** **
      **
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • -**
    )^ +** ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result @@ -3596,17 +4356,26 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. -** ^If the statement was executed successfully or not executed at all, then -** SQLITE_OK is returned. ^If execution of the statement failed then an -** [error code] or [extended error code] is returned. +** ^If the most recent evaluation of the statement encountered no errors +** or if the statement is never been evaluated, then sqlite3_finalize() returns +** SQLITE_OK. ^If the most recent evaluation of statement S failed, then +** sqlite3_finalize(S) returns the appropriate [error code] or +** [extended error code]. ** -** ^This routine can be called at any point during the execution of the -** [prepared statement]. ^If the virtual machine has not -** completed execution when this routine is called, that is like -** encountering an error or an [sqlite3_interrupt | interrupt]. -** ^Incomplete updates may be rolled back and transactions canceled, -** depending on the circumstances, and the -** [error code] returned will be [SQLITE_ABORT]. +** ^The sqlite3_finalize(S) routine can be called at any point during +** the life cycle of [prepared statement] S: +** before statement S is ever evaluated, after +** one or more calls to [sqlite3_reset()], or after any call +** to [sqlite3_step()] regardless of whether or not the statement has +** completed execution. +** +** ^Invoking sqlite3_finalize() on a NULL pointer is a harmless no-op. +** +** The application must finalize every [prepared statement] in order to avoid +** resource leaks. It is a grievous error for the application to try to use +** a prepared statement after it has been finalized. Any use of a prepared +** statement after it has been finalized can result in undefined and +** undesirable behavior such as segfaults and heap corruption. */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); @@ -3642,23 +4411,25 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** -** ^These two functions (collectively known as "function creation routines") +** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior -** of existing SQL functions or aggregates. The only difference between the -** two is that the second parameter, the name of the (scalar) function or -** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16 -** for sqlite3_create_function16(). +** of existing SQL functions or aggregates. The only differences between +** these routines are the text encoding expected for +** the second parameter (the name of the function being created) +** and the presence or absence of a destructor callback for +** the application data pointer. ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database ** connection then application-defined SQL functions must be added ** to each database connection separately. ** -** The second parameter is the name of the SQL function to be created or -** redefined. ^The length of the name is limited to 255 bytes, exclusive of -** the zero-terminator. Note that the name length limit is in bytes, not -** characters. ^Any attempt to create a function with a longer name -** will result in [SQLITE_ERROR] being returned. +** ^The second parameter is the name of the SQL function to be created or +** redefined. ^The length of the name is limited to 255 bytes in a UTF-8 +** representation, exclusive of the zero-terminator. ^Note that the name +** length limit is in UTF-8 bytes, not characters nor UTF-16 bytes. +** ^Any attempt to create a function with a longer name +** will result in [SQLITE_MISUSE] being returned. ** ** ^The third parameter (nArg) ** is the number of arguments that the SQL function or @@ -3668,10 +4439,10 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** -** The fourth parameter, eTextRep, specifies what +** ^The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for -** its parameters. Any SQL function implementation should be able to work -** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be +** its parameters. Every SQL function implementation must be able to work +** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be ** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. @@ -3683,13 +4454,24 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** -** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are +** ^The sixth, seventh and eighth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc -** callback only; NULL pointers should be passed as the xStep and xFinal +** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep -** and xFinal and NULL should be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL for all three function callbacks. +** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing +** SQL function or aggregate, pass NULL pointers for all three function +** callbacks. +** +** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, +** then it is destructor for the application data pointer. +** The destructor is invoked when the function is deleted, either by being +** overloaded or when the database connection closes.)^ +** ^The destructor is also invoked if the call to +** sqlite3_create_function_v2() fails. +** ^When the destructor callback of the tenth parameter is invoked, it +** is passed a single argument which is a copy of the application data +** pointer which was the fifth parameter to sqlite3_create_function_v2(). ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of @@ -3705,11 +4487,6 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); ** between UTF8 and UTF16. ** ** ^Built-in functions may be overloaded by new application-defined functions. -** ^The first application-defined function with a given name overrides all -** built-in functions in the same [database connection] with the same name. -** ^Subsequent application-defined functions of the same name only override -** prior application-defined functions that are an exact match for the -** number of parameters and preferred encoding. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not @@ -3736,6 +4513,17 @@ SQLITE_API int sqlite3_create_function16( void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); +SQLITE_API int sqlite3_create_function_v2( + sqlite3 *db, + const char *zFunctionName, + int nArg, + int eTextRep, + void *pApp, + void (*xFunc)(sqlite3_context*,int,sqlite3_value**), + void (*xStep)(sqlite3_context*,int,sqlite3_value**), + void (*xFinal)(sqlite3_context*), + void(*xDestroy)(void*) +); /* ** CAPI3REF: Text Encodings @@ -3779,7 +4567,7 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6 ** The xFunc (for scalar functions) or xStep (for aggregates) parameters ** to [sqlite3_create_function()] and [sqlite3_create_function16()] ** define callbacks that implement the SQL functions and aggregates. -** The 4th parameter to these callbacks is an array of pointers to +** The 3rd parameter to these callbacks is an array of pointers to ** [protected sqlite3_value] objects. There is one [sqlite3_value] object for ** each parameter to the SQL function. These routines are used to ** extract values from the [sqlite3_value] objects. @@ -3830,7 +4618,7 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** -** Implementions of aggregate SQL functions use this +** Implementations of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** ** ^The first time the sqlite3_aggregate_context(C,N) routine is called @@ -4004,11 +4792,11 @@ typedef void (*sqlite3_destructor_type)(void*); ** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() ** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. ** -** ^The sqlite3_result_toobig() interface causes SQLite to throw an error -** indicating that a string or BLOB is too long to represent. +** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an +** error indicating that a string or BLOB is too long to represent. ** -** ^The sqlite3_result_nomem() interface causes SQLite to throw an error -** indicating that a memory allocation failed. +** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an +** error indicating that a memory allocation failed. ** ** ^The sqlite3_result_int() interface sets the return value ** of the application-defined function to be the 32-bit signed integer @@ -4033,7 +4821,12 @@ typedef void (*sqlite3_destructor_type)(void*); ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined -** function result. +** function result. If the 3rd parameter is non-negative, then it +** must be the byte offset into the string where the NUL terminator would +** appear if the string where NUL terminated. If any NUL characters occur +** in the string at a byte offset that is less than the value of the 3rd +** parameter, then the resulting string will contain embedded NULs and the +** result of expressions operating on strings with embedded NULs is undefined. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has @@ -4082,46 +4875,79 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** -** These functions are used to add new collation sequences to the -** [database connection] specified as the first argument. +** ^These functions add, remove, or modify a [collation] associated +** with the [database connection] specified as the first argument. ** -** ^The name of the new collation sequence is specified as a UTF-8 string +** ^The name of the collation is a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() -** and a UTF-16 string for sqlite3_create_collation16(). ^In all cases -** the name is passed as the second function argument. +** and a UTF-16 string in native byte order for sqlite3_create_collation16(). +** ^Collation names that compare equal according to [sqlite3_strnicmp()] are +** considered to be the same name. ** -** ^The third argument may be one of the constants [SQLITE_UTF8], -** [SQLITE_UTF16LE], or [SQLITE_UTF16BE], indicating that the user-supplied -** routine expects to be passed pointers to strings encoded using UTF-8, -** UTF-16 little-endian, or UTF-16 big-endian, respectively. ^The -** third argument might also be [SQLITE_UTF16] to indicate that the routine -** expects pointers to be UTF-16 strings in the native byte order, or the -** argument can be [SQLITE_UTF16_ALIGNED] if the -** the routine expects pointers to 16-bit word aligned strings -** of UTF-16 in the native byte order. +** ^(The third argument (eTextRep) must be one of the constants: +**
      +**
    • [SQLITE_UTF8], +**
    • [SQLITE_UTF16LE], +**
    • [SQLITE_UTF16BE], +**
    • [SQLITE_UTF16], or +**
    • [SQLITE_UTF16_ALIGNED]. +**
    )^ +** ^The eTextRep argument determines the encoding of strings passed +** to the collating function callback, xCallback. +** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep +** force strings to be UTF16 with native byte order. +** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin +** on an even byte address. ** -** A pointer to the user supplied routine must be passed as the fifth -** argument. ^If it is NULL, this is the same as deleting the collation -** sequence (so that SQLite cannot call it anymore). -** ^Each time the application supplied function is invoked, it is passed -** as its first parameter a copy of the void* passed as the fourth argument -** to sqlite3_create_collation() or sqlite3_create_collation16(). +** ^The fourth argument, pArg, is an application data pointer that is passed +** through as the first argument to the collating function callback. ** -** ^The remaining arguments to the application-supplied routine are two strings, -** each represented by a (length, data) pair and encoded in the encoding -** that was passed as the third argument when the collation sequence was -** registered. The application defined collation routine should -** return negative, zero or positive if the first string is less than, -** equal to, or greater than the second string. i.e. (STRING1 - STRING2). +** ^The fifth argument, xCallback, is a pointer to the collating function. +** ^Multiple collating functions can be registered using the same name but +** with different eTextRep parameters and SQLite will use whichever +** function requires the least amount of data transformation. +** ^If the xCallback argument is NULL then the collating function is +** deleted. ^When all collating functions having the same name are deleted, +** that collation is no longer usable. +** +** ^The collating function callback is invoked with a copy of the pArg +** application data pointer and with two strings in the encoding specified +** by the eTextRep argument. The collating function must return an +** integer that is negative, zero, or positive +** if the first string is less than, equal to, or greater than the second, +** respectively. A collating function must always return the same answer +** given the same inputs. If two or more collating functions are registered +** to the same collation name (using different eTextRep values) then all +** must give an equivalent answer when invoked with equivalent strings. +** The collating function must obey the following properties for all +** strings A, B, and C: +** +**
      +**
    1. If A==B then B==A. +**
    2. If A==B and B==C then A==C. +**
    3. If A<B THEN B>A. +**
    4. If A<B and B<C then A<C. +**
    +** +** If a collating function fails any of the above constraints and that +** collating function is registered and used, then the behavior of SQLite +** is undefined. ** ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() -** except that it takes an extra argument which is a destructor for -** the collation. ^The destructor is called when the collation is -** destroyed and is passed a copy of the fourth parameter void* pointer -** of the sqlite3_create_collation_v2(). -** ^Collations are destroyed when they are overridden by later calls to the -** collation creation functions or when the [database connection] is closed -** using [sqlite3_close()]. +** with the addition that the xDestroy callback is invoked on pArg when +** the collating function is deleted. +** ^Collating functions are deleted when they are overridden by later +** calls to the collation creation functions or when the +** [database connection] is closed using [sqlite3_close()]. +** +** ^The xDestroy callback is not called if the +** sqlite3_create_collation_v2() function fails. Applications that invoke +** sqlite3_create_collation_v2() with a non-NULL xDestroy argument should +** check the return code and dispose of the application data pointer +** themselves rather than expecting SQLite to deal with it for them. +** This is different from every other SQLite interface. The inconsistency +** is unfortunate but cannot be changed without breaking backwards +** compatibility. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ @@ -4129,14 +4955,14 @@ SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, - void*, + void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, - void*, + void *pArg, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); @@ -4144,7 +4970,7 @@ SQLITE_API int sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, - void*, + void *pArg, int(*xCompare)(void*,int,const void*,int,const void*) ); @@ -4185,6 +5011,7 @@ SQLITE_API int sqlite3_collation_needed16( void(*)(void*,sqlite3*,int eTextRep,const void*) ); +#ifdef SQLITE_HAS_CODEC /* ** Specify the key for an encrypted database. This routine should be ** called right after sqlite3_open(). @@ -4210,19 +5037,41 @@ SQLITE_API int sqlite3_rekey( const void *pKey, int nKey /* The new key */ ); +/* +** Specify the activation key for a SEE database. Unless +** activated, none of the SEE routines will work. +*/ +SQLITE_API void sqlite3_activate_see( + const char *zPassPhrase /* Activation phrase */ +); +#endif + +#ifdef SQLITE_ENABLE_CEROD +/* +** Specify the activation key for a CEROD database. Unless +** activated, none of the CEROD routines will work. +*/ +SQLITE_API void sqlite3_activate_cerod( + const char *zPassPhrase /* Activation phrase */ +); +#endif + /* ** CAPI3REF: Suspend Execution For A Short Time ** -** ^The sqlite3_sleep() function causes the current thread to suspend execution +** The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** -** ^If the operating system does not support sleep requests with +** If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to -** the nearest second. ^The number of milliseconds of sleep actually +** the nearest second. The number of milliseconds of sleep actually ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() -** method of the default [sqlite3_vfs] object. +** method of the default [sqlite3_vfs] object. If the xSleep() method +** of the default VFS is not implemented correctly, or not implemented at +** all, then the behavior of sqlite3_sleep() may deviate from the description +** in the previous paragraphs. */ SQLITE_API int sqlite3_sleep(int); @@ -4254,9 +5103,61 @@ SQLITE_API int sqlite3_sleep(int); ** Hence, if this variable is modified directly, either it should be ** made NULL or made to point to memory obtained from [sqlite3_malloc] ** or else the use of the [temp_store_directory pragma] should be avoided. +** +** Note to Windows Runtime users: The temporary directory must be set +** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various +** features that require the use of temporary files may fail. Here is an +** example of how to do this using C++ with the Windows Runtime: +** +**
    +** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
    +**       TemporaryFolder->Path->Data();
    +** char zPathBuf[MAX_PATH + 1];
    +** memset(zPathBuf, 0, sizeof(zPathBuf));
    +** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
    +**       NULL, NULL);
    +** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
    +** 
    */ SQLITE_API char *sqlite3_temp_directory; +/* +** CAPI3REF: Name Of The Folder Holding Database Files +** +** ^(If this global variable is made to point to a string which is +** the name of a folder (a.k.a. directory), then all database files +** specified with a relative pathname and created or accessed by +** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed +** to be relative to that directory.)^ ^If this variable is a NULL +** pointer, then SQLite assumes that all database files specified +** with a relative pathname are relative to the current directory +** for the process. Only the windows VFS makes use of this global +** variable; it is ignored by the unix VFS. +** +** Changing the value of this variable while a database connection is +** open can result in a corrupt database. +** +** It is not safe to read or modify this variable in more than one +** thread at a time. It is not safe to read or modify this variable +** if a [database connection] is being used at the same time in a separate +** thread. +** It is intended that this variable be set once +** as part of process initialization and before any SQLite interface +** routines have been called and that this variable remain unchanged +** thereafter. +** +** ^The [data_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, +** the [data_store_directory pragma] always assumes that any string +** that this variable points to is held in memory obtained from +** [sqlite3_malloc] and the pragma may attempt to free that memory +** using [sqlite3_free]. +** Hence, if this variable is modified directly, either it should be +** made NULL or made to point to memory obtained from [sqlite3_malloc] +** or else the use of the [data_store_directory pragma] should be avoided. +*/ +SQLITE_API char *sqlite3_data_directory; + /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} @@ -4292,6 +5193,31 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*); */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +/* +** CAPI3REF: Return The Filename For A Database Connection +** +** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename +** associated with database N of connection D. ^The main database file +** has the name "main". If there is no attached database N on the database +** connection D, or if database N is a temporary or in-memory database, then +** a NULL pointer is returned. +** +** ^The filename returned by this function is the output of the +** xFullPathname method of the [VFS]. ^In other words, the filename +** will be an absolute pathname, even if the filename used +** to open the database originally was a URI or relative pathname. +*/ +SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); + +/* +** CAPI3REF: Determine if a database is read-only +** +** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N +** of connection D is read-only, 0 if it is read/write, or -1 if N is not +** the name of a database on connection D. +*/ +SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName); + /* ** CAPI3REF: Find the next prepared statement ** @@ -4327,13 +5253,15 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** on the same [database connection] D, or NULL for ** the first call for each function on D. ** +** The commit and rollback hook callbacks are not reentrant. ** The callback implementation must not do anything that will modify ** the database connection that invoked the callback. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the commit ** or rollback hook in the first place. -** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their -** database connections for the meaning of "modify" in this paragraph. +** Note that running any other SQL statements, including SELECT statements, +** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify +** the database connections for the meaning of "modify" in this paragraph. ** ** ^Registering a NULL function disables the callback. ** @@ -4348,8 +5276,6 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); ** an error or constraint causes an implicit rollback to occur. ** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. -** ^The rollback callback is not invoked if a transaction is -** rolled back because a commit callback returned non-zero. ** ** See also the [sqlite3_update_hook()] interface. */ @@ -4410,7 +5336,6 @@ SQLITE_API void *sqlite3_update_hook( /* ** CAPI3REF: Enable Or Disable Shared Pager Cache -** KEYWORDS: {shared cache} ** ** ^(This routine enables or disables the sharing of the database cache ** and schema data structures between [database connection | connections] @@ -4433,6 +5358,9 @@ SQLITE_API void *sqlite3_update_hook( ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** +** This interface is threadsafe on processors where writing a +** 32-bit integer is atomic. +** ** See Also: [SQLite Shared-Cache Mode] */ SQLITE_API int sqlite3_enable_shared_cache(int); @@ -4446,40 +5374,89 @@ SQLITE_API int sqlite3_enable_shared_cache(int); ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. +** ^The sqlite3_release_memory() routine is a no-op returning zero +** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** See also: [sqlite3_db_release_memory()] */ SQLITE_API int sqlite3_release_memory(int); +/* +** CAPI3REF: Free Memory Used By A Database Connection +** +** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap +** memory as possible from database connection D. Unlike the +** [sqlite3_release_memory()] interface, this interface is effect even +** when then [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is +** omitted. +** +** See also: [sqlite3_release_memory()] +*/ +SQLITE_API int sqlite3_db_release_memory(sqlite3*); + /* ** CAPI3REF: Impose A Limit On Heap Size ** -** ^The sqlite3_soft_heap_limit() interface places a "soft" limit -** on the amount of heap memory that may be allocated by SQLite. -** ^If an internal allocation is requested that would exceed the -** soft heap limit, [sqlite3_release_memory()] is invoked one or -** more times to free up some space before the allocation is performed. +** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the +** soft limit on the amount of heap memory that may be allocated by SQLite. +** ^SQLite strives to keep heap memory utilization below the soft heap +** limit by reducing the number of pages held in the page cache +** as heap memory usages approaches the limit. +** ^The soft heap limit is "soft" because even though SQLite strives to stay +** below the limit, it will exceed the limit rather than generate +** an [SQLITE_NOMEM] error. In other words, the soft heap limit +** is advisory only. ** -** ^The limit is called "soft" because if [sqlite3_release_memory()] -** cannot free sufficient memory to prevent the limit from being exceeded, -** the memory is allocated anyway and the current operation proceeds. +** ^The return value from sqlite3_soft_heap_limit64() is the size of +** the soft heap limit prior to the call, or negative in the case of an +** error. ^If the argument N is negative +** then no change is made to the soft heap limit. Hence, the current +** size of the soft heap limit can be determined by invoking +** sqlite3_soft_heap_limit64() with a negative argument. ** -** ^A negative or zero value for N means that there is no soft heap limit and -** [sqlite3_release_memory()] will only be called when memory is exhausted. -** ^The default value for the soft heap limit is zero. +** ^If the argument N is zero then the soft heap limit is disabled. ** -** ^(SQLite makes a best effort to honor the soft heap limit. -** But if the soft heap limit cannot be honored, execution will -** continue without error or notification.)^ This is why the limit is -** called a "soft" limit. It is advisory only. +** ^(The soft heap limit is not enforced in the current implementation +** if one or more of following conditions are true: ** -** Prior to SQLite version 3.5.0, this routine only constrained the memory -** allocated by a single thread - the same thread in which this routine -** runs. Beginning with SQLite version 3.5.0, the soft heap limit is -** applied to all threads. The value specified for the soft heap limit -** is an upper bound on the total memory allocation for all threads. In -** version 3.5.0 there is no mechanism for limiting the heap usage for -** individual threads. +**
      +**
    • The soft heap limit is set to zero. +**
    • Memory accounting is disabled using a combination of the +** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and +** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. +**
    • An alternative page cache implementation is specified using +** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...). +**
    • The page cache allocates from its own memory pool supplied +** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than +** from the heap. +**
    )^ +** +** Beginning with SQLite version 3.7.3, the soft heap limit is enforced +** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT] +** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT], +** the soft heap limit is enforced on every memory allocation. Without +** [SQLITE_ENABLE_MEMORY_MANAGEMENT], the soft heap limit is only enforced +** when memory is allocated by the page cache. Testing suggests that because +** the page cache is the predominate memory user in SQLite, most +** applications will achieve adequate soft heap limit enforcement without +** the use of [SQLITE_ENABLE_MEMORY_MANAGEMENT]. +** +** The circumstances under which SQLite will enforce the soft heap limit may +** changes in future releases of SQLite. */ -SQLITE_API void sqlite3_soft_heap_limit(int); +SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N); + +/* +** CAPI3REF: Deprecated Soft Heap Limit Interface +** DEPRECATED +** +** This is a deprecated version of the [sqlite3_soft_heap_limit64()] +** interface. This routine is provided for historical compatibility +** only. All new applications should use the +** [sqlite3_soft_heap_limit64()] interface rather than this one. +*/ +SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); + /* ** CAPI3REF: Extract Metadata About A Column Of A Table @@ -4603,40 +5580,51 @@ SQLITE_API int sqlite3_load_extension( SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* -** CAPI3REF: Automatically Load An Extensions +** CAPI3REF: Automatically Load Statically Linked Extensions ** -** ^This API can be invoked at program startup in order to register -** one or more statically linked extensions that will be available -** to all new [database connections]. +** ^This interface causes the xEntryPoint() function to be invoked for +** each new [database connection] that is created. The idea here is that +** xEntryPoint() is the entry point for a statically linked SQLite extension +** that is to be automatically loaded into all new database connections. ** -** ^(This routine stores a pointer to the extension entry point -** in an array that is obtained from [sqlite3_malloc()]. That memory -** is deallocated by [sqlite3_reset_auto_extension()].)^ +** ^(Even though the function prototype shows that xEntryPoint() takes +** no arguments and returns void, SQLite invokes xEntryPoint() with three +** arguments and expects and integer result as if the signature of the +** entry point where as follows: ** -** ^This function registers an extension entry point that is -** automatically invoked whenever a new [database connection] -** is opened using [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()]. -** ^Duplicate extensions are detected so calling this routine -** multiple times with the same extension is harmless. -** ^Automatic extensions apply across all threads. +**
    +**    int xEntryPoint(
    +**      sqlite3 *db,
    +**      const char **pzErrMsg,
    +**      const struct sqlite3_api_routines *pThunk
    +**    );
    +** 
    )^ +** +** If the xEntryPoint routine encounters an error, it should make *pzErrMsg +** point to an appropriate error message (obtained from [sqlite3_mprintf()]) +** and return an appropriate [error code]. ^SQLite ensures that *pzErrMsg +** is NULL before calling the xEntryPoint(). ^SQLite will invoke +** [sqlite3_free()] on *pzErrMsg after xEntryPoint() returns. ^If any +** xEntryPoint() returns an error, the [sqlite3_open()], [sqlite3_open16()], +** or [sqlite3_open_v2()] call that provoked the xEntryPoint() will fail. +** +** ^Calling sqlite3_auto_extension(X) with an entry point X that is already +** on the list of automatic extensions is a harmless no-op. ^No entry point +** will be called more than once for each database connection that is opened. +** +** See also: [sqlite3_reset_auto_extension()]. */ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading ** -** ^(This function disables all previously registered automatic -** extensions. It undoes the effect of all prior -** [sqlite3_auto_extension()] calls.)^ -** -** ^This function disables automatic extensions in all threads. +** ^This interface disables all automatic extensions previously +** registered using [sqlite3_auto_extension()]. */ SQLITE_API void sqlite3_reset_auto_extension(void); /* -****** EXPERIMENTAL - subject to change without notice ************** -** ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. @@ -4656,9 +5644,8 @@ typedef struct sqlite3_module sqlite3_module; /* ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} -** EXPERIMENTAL ** -** This structure, sometimes called a a "virtual table module", +** This structure, sometimes called a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** @@ -4698,14 +5685,19 @@ struct sqlite3_module { void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); + /* The methods above are in version 1 of the sqlite_module object. Those + ** below are for version 2 and greater. */ + int (*xSavepoint)(sqlite3_vtab *pVTab, int); + int (*xRelease)(sqlite3_vtab *pVTab, int); + int (*xRollbackTo)(sqlite3_vtab *pVTab, int); }; /* ** CAPI3REF: Virtual Table Indexing Information ** KEYWORDS: sqlite3_index_info -** EXPERIMENTAL ** -** The sqlite3_index_info structure and its substructures is used to +** The sqlite3_index_info structure and its substructures is used as part +** of the [virtual table] interface to ** pass information into and receive the reply from the [xBestIndex] ** method of a [virtual table module]. The fields under **Inputs** are the ** inputs to xBestIndex and are read-only. xBestIndex inserts its @@ -4713,10 +5705,12 @@ struct sqlite3_module { ** ** ^(The aConstraint[] array records WHERE clause constraints of the form: ** -**
    column OP expr
    +**
    column OP expr
    ** ** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is -** stored in aConstraint[].op.)^ ^(The index of the column is stored in +** stored in aConstraint[].op using one of the +** [SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_ values].)^ +** ^(The index of the column is stored in ** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the ** expr on the right-hand side can be evaluated (and thus the constraint ** is usable) and false if it cannot.)^ @@ -4776,6 +5770,15 @@ struct sqlite3_index_info { int orderByConsumed; /* True if output is already ordered */ double estimatedCost; /* Estimated cost of using this index */ }; + +/* +** CAPI3REF: Virtual Table Constraint Operator Codes +** +** These macros defined the allowed values for the +** [sqlite3_index_info].aConstraint[].op field. Each value represents +** an operator that is part of a constraint term in the wHERE clause of +** a query that uses a [virtual table]. +*/ #define SQLITE_INDEX_CONSTRAINT_EQ 2 #define SQLITE_INDEX_CONSTRAINT_GT 4 #define SQLITE_INDEX_CONSTRAINT_LE 8 @@ -4785,7 +5788,6 @@ struct sqlite3_index_info { /* ** CAPI3REF: Register A Virtual Table Implementation -** EXPERIMENTAL ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before @@ -4803,17 +5805,19 @@ struct sqlite3_index_info { ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite -** no longer needs the pClientData pointer. ^The sqlite3_create_module() +** no longer needs the pClientData pointer. ^The destructor will also +** be invoked if the call to sqlite3_create_module_v2() fails. +** ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module( +SQLITE_API int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( +SQLITE_API int sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ @@ -4824,7 +5828,6 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( /* ** CAPI3REF: Virtual Table Instance Object ** KEYWORDS: sqlite3_vtab -** EXPERIMENTAL ** ** Every [virtual table module] implementation uses a subclass ** of this object to describe a particular instance @@ -4850,7 +5853,6 @@ struct sqlite3_vtab { /* ** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} -** EXPERIMENTAL ** ** Every [virtual table module] implementation uses a subclass of the ** following structure to describe cursors that point into the @@ -4872,18 +5874,16 @@ struct sqlite3_vtab_cursor { /* ** CAPI3REF: Declare The Schema Of A Virtual Table -** EXPERIMENTAL ** ** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zSQL); +SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table -** EXPERIMENTAL ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. @@ -4898,7 +5898,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zS ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); +SQLITE_API int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up @@ -4908,8 +5908,6 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const cha ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. -** -****** EXPERIMENTAL - subject to change without notice ************** */ /* @@ -4964,7 +5962,7 @@ typedef struct sqlite3_blob sqlite3_blob; ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ @@ -4992,6 +5990,30 @@ SQLITE_API int sqlite3_blob_open( sqlite3_blob **ppBlob ); +/* +** CAPI3REF: Move a BLOB Handle to a New Row +** +** ^This function is used to move an existing blob handle so that it points +** to a different row of the same database table. ^The new row is identified +** by the rowid value passed as the second argument. Only the row can be +** changed. ^The database, table and column on which the blob handle is open +** remain the same. Moving an existing blob handle to a new row can be +** faster than closing the existing handle and opening a new one. +** +** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] - +** it must exist and there must be either a blob or text value stored in +** the nominated column.)^ ^If the new row is not present in the table, or if +** it does not contain a blob or text value, or if another error occurs, an +** SQLite error code is returned and the blob handle is considered aborted. +** ^All subsequent calls to [sqlite3_blob_read()], [sqlite3_blob_write()] or +** [sqlite3_blob_reopen()] on an aborted blob handle immediately return +** SQLITE_ABORT. ^Calling [sqlite3_blob_bytes()] on an aborted blob handle +** always returns zero. +** +** ^This function sets the database handle error code and message. +*/ +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64); + /* ** CAPI3REF: Close A BLOB Handle ** @@ -5144,17 +6166,16 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); ** implementations are available in the SQLite core: ** **
      -**
    • SQLITE_MUTEX_OS2 -**
    • SQLITE_MUTEX_PTHREAD +**
    • SQLITE_MUTEX_PTHREADS **
    • SQLITE_MUTEX_W32 **
    • SQLITE_MUTEX_NOOP **
    )^ ** ** ^The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in -** a single-threaded application. ^The SQLITE_MUTEX_OS2, -** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations -** are appropriate for use on OS/2, Unix, and Windows. +** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and +** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix +** and Windows. ** ** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex @@ -5252,7 +6273,6 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object -** EXPERIMENTAL ** ** An instance of this structure defines the low-level routines ** used to allocate and use mutexes. @@ -5269,7 +6289,7 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. -** ^The xMutexInit routine is calle by SQLite exactly once for each +** ^The xMutexInit routine is called by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** ** ^The xMutexEnd method defined by this structure is invoked as @@ -5302,7 +6322,7 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to -** invoke xMutexInit() mutiple times within the same process and without +** invoke xMutexInit() multiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** @@ -5344,14 +6364,14 @@ struct sqlite3_mutex_methods { ** ^These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** -** ^The implementation is not required to provided versions of these +** ^The implementation is not required to provide versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then ** the routine should return 1. This seems counter-intuitive since -** clearly the mutex cannot be held if it does not exist. But the +** clearly the mutex cannot be held if it does not exist. But ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is @@ -5381,7 +6401,8 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ -#define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ +#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */ +#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */ /* ** CAPI3REF: Retrieve the mutex for a database connection @@ -5400,7 +6421,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The -** name of the database "main" for the main database or "temp" for the +** name of the database is "main" for the main database or "temp" for the ** TEMP database, or the name that appears after the AS keyword for ** databases that are added using the [ATTACH] SQL command. ** ^A NULL pointer can be used in place of "main" to refer to the @@ -5410,6 +6431,12 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); ** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** +** ^The SQLITE_FCNTL_FILE_POINTER value for the op parameter causes +** a pointer to the underlying [sqlite3_file] object to be written into +** the space pointed to by the 4th parameter. ^The SQLITE_FCNTL_FILE_POINTER +** case is a short-circuit path which does not actually invoke the +** underlying sqlite3_io_methods.xFileControl method. +** ** ^If the second parameter (zDbName) does not match the name of any ** open database file, then SQLITE_ERROR is returned. ^This error ** code is not remembered and will not be recalled by [sqlite3_errcode()] @@ -5465,17 +6492,19 @@ SQLITE_API int sqlite3_test_control(int op, ...); #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 -#define SQLITE_TESTCTRL_LAST 16 +#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18 +#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 +#define SQLITE_TESTCTRL_LAST 19 /* ** CAPI3REF: SQLite Runtime Status -** EXPERIMENTAL ** ** ^This interface is used to retrieve runtime status information -** about the preformance of SQLite, and optionally to reset various +** about the performance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes -** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ +** are of the form [status parameters | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after @@ -5485,7 +6514,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a +** ^The sqlite3_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** This routine is threadsafe but is not atomic. This routine can be @@ -5497,18 +6526,18 @@ SQLITE_API int sqlite3_test_control(int op, ...); ** ** See also: [sqlite3_db_status()] */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); /* ** CAPI3REF: Status Parameters -** EXPERIMENTAL +** KEYWORDS: {status parameters} ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** **
    -** ^(
    SQLITE_STATUS_MEMORY_USED
    +** [[SQLITE_STATUS_MEMORY_USED]] ^(
    SQLITE_STATUS_MEMORY_USED
    **
    This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application @@ -5518,35 +6547,40 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pH ** this parameter. The amount returned is the sum of the allocation ** sizes as reported by the xSize method in [sqlite3_mem_methods].
    )^ ** -** ^(
    SQLITE_STATUS_MALLOC_SIZE
    +** [[SQLITE_STATUS_MALLOC_SIZE]] ^(
    SQLITE_STATUS_MALLOC_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their ** internal equivalents). Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.
    )^ ** -** ^(
    SQLITE_STATUS_PAGECACHE_USED
    +** [[SQLITE_STATUS_MALLOC_COUNT]] ^(
    SQLITE_STATUS_MALLOC_COUNT
    +**
    This parameter records the number of separate memory allocations +** currently checked out.
    )^ +** +** [[SQLITE_STATUS_PAGECACHE_USED]] ^(
    SQLITE_STATUS_PAGECACHE_USED
    **
    This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.
    )^ ** +** [[SQLITE_STATUS_PAGECACHE_OVERFLOW]] ** ^(
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    **
    This parameter returns the number of bytes of page cache -** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE] +** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.
    )^ ** -** ^(
    SQLITE_STATUS_PAGECACHE_SIZE
    +** [[SQLITE_STATUS_PAGECACHE_SIZE]] ^(
    SQLITE_STATUS_PAGECACHE_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.
    )^ ** -** ^(
    SQLITE_STATUS_SCRATCH_USED
    +** [[SQLITE_STATUS_SCRATCH_USED]] ^(
    SQLITE_STATUS_SCRATCH_USED
    **
    This parameter returns the number of allocations used out of the ** [scratch memory allocator] configured using ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not @@ -5554,9 +6588,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pH ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.
    )^ ** -** ^(
    SQLITE_STATUS_SCRATCH_OVERFLOW
    +** [[SQLITE_STATUS_SCRATCH_OVERFLOW]] ^(
    SQLITE_STATUS_SCRATCH_OVERFLOW
    **
    This parameter returns the number of bytes of scratch memory -** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH] +** allocation which could not be satisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values ** returned include overflows because the requested allocation was too ** larger (that is, because the requested allocation was larger than the @@ -5564,13 +6598,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pH ** slots were available. **
    )^ ** -** ^(
    SQLITE_STATUS_SCRATCH_SIZE
    +** [[SQLITE_STATUS_SCRATCH_SIZE]] ^(
    SQLITE_STATUS_SCRATCH_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [scratch memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.
    )^ ** -** ^(
    SQLITE_STATUS_PARSER_STACK
    +** [[SQLITE_STATUS_PARSER_STACK]] ^(
    SQLITE_STATUS_PARSER_STACK
    **
    This parameter records the deepest parser stack. It is only ** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
    )^ **
    @@ -5586,30 +6620,35 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pH #define SQLITE_STATUS_PARSER_STACK 6 #define SQLITE_STATUS_PAGECACHE_SIZE 7 #define SQLITE_STATUS_SCRATCH_SIZE 8 +#define SQLITE_STATUS_MALLOC_COUNT 9 /* ** CAPI3REF: Database Connection Status -** EXPERIMENTAL ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument -** is the parameter to interrogate. ^Currently, the only allowed value -** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED]. -** Additional options will likely appear in future releases of SQLite. +** is an integer constant, taken from the set of +** [SQLITE_DBSTATUS options], that +** determines the parameter to interrogate. The set of +** [SQLITE_DBSTATUS options] is likely +** to grow in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** +** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. +** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections -** EXPERIMENTAL +** KEYWORDS: {SQLITE_DBSTATUS options} ** ** These constants are the available integer "verbs" that can be passed as ** the second argument to the [sqlite3_db_status()] interface. @@ -5621,20 +6660,94 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur ** if a discontinued or unsupported verb is invoked. ** **
    -** ^(
    SQLITE_DBSTATUS_LOOKASIDE_USED
    +** [[SQLITE_DBSTATUS_LOOKASIDE_USED]] ^(
    SQLITE_DBSTATUS_LOOKASIDE_USED
    **
    This parameter returns the number of lookaside memory slots currently ** checked out.
    )^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(
    SQLITE_DBSTATUS_LOOKASIDE_HIT
    +**
    This parameter returns the number malloc attempts that were +** satisfied using lookaside memory. Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]] +** ^(
    SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE
    +**
    This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to the amount of +** memory requested being larger than the lookaside slot size. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]] +** ^(
    SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL
    +**
    This parameter returns the number malloc attempts that might have +** been satisfied using lookaside memory but failed due to all lookaside +** memory already being in use. +** Only the high-water value is meaningful; +** the current value is always zero.)^ +** +** [[SQLITE_DBSTATUS_CACHE_USED]] ^(
    SQLITE_DBSTATUS_CACHE_USED
    +**
    This parameter returns the approximate number of of bytes of heap +** memory used by all pager caches associated with the database connection.)^ +** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0. +** +** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(
    SQLITE_DBSTATUS_SCHEMA_USED
    +**
    This parameter returns the approximate number of of bytes of heap +** memory used to store the schema for all databases associated +** with the connection - main, temp, and any [ATTACH]-ed databases.)^ +** ^The full amount of memory used by the schemas is reported, even if the +** schema memory is shared with other database connections due to +** [shared cache mode] being enabled. +** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0. +** +** [[SQLITE_DBSTATUS_STMT_USED]] ^(
    SQLITE_DBSTATUS_STMT_USED
    +**
    This parameter returns the approximate number of of bytes of heap +** and lookaside memory used by all prepared statements associated with +** the database connection.)^ +** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0. +**
    +** +** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(
    SQLITE_DBSTATUS_CACHE_HIT
    +**
    This parameter returns the number of pager cache hits that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT +** is always 0. +**
    +** +** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(
    SQLITE_DBSTATUS_CACHE_MISS
    +**
    This parameter returns the number of pager cache misses that have +** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS +** is always 0. +**
    +** +** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(
    SQLITE_DBSTATUS_CACHE_WRITE
    +**
    This parameter returns the number of dirty cache entries that have +** been written to disk. Specifically, the number of pages written to the +** wal file in wal mode databases, or the number of pages written to the +** database file in rollback mode databases. Any pages written as part of +** transaction rollback or database recovery operations are not included. +** If an IO or other error occurs while writing a page to disk, the effect +** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The +** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. +**
    **
    */ -#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_LOOKASIDE_USED 0 +#define SQLITE_DBSTATUS_CACHE_USED 1 +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 +#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 +#define SQLITE_DBSTATUS_CACHE_HIT 7 +#define SQLITE_DBSTATUS_CACHE_MISS 8 +#define SQLITE_DBSTATUS_CACHE_WRITE 9 +#define SQLITE_DBSTATUS_MAX 9 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status -** EXPERIMENTAL ** ** ^(Each prepared statement maintains various -** [SQLITE_STMTSTATUS_SORT | counters] that measure the number +** [SQLITE_STMTSTATUS counters] that measure the number ** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds @@ -5645,7 +6758,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur ** ^(This interface is used to retrieve and reset counter values from ** a [prepared statement]. The first argument is the prepared statement ** object to be interrogated. The second argument -** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] +** is an integer code for a specific [SQLITE_STMTSTATUS counter] ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this @@ -5653,36 +6766,42 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ -SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements -** EXPERIMENTAL +** KEYWORDS: {SQLITE_STMTSTATUS counter} {SQLITE_STMTSTATUS counters} ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite3_stmt_status()] interface. ** The meanings of the various counters are as follows: ** **
    -**
    SQLITE_STMTSTATUS_FULLSCAN_STEP
    +** [[SQLITE_STMTSTATUS_FULLSCAN_STEP]]
    SQLITE_STMTSTATUS_FULLSCAN_STEP
    **
    ^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.
    ** -**
    SQLITE_STMTSTATUS_SORT
    +** [[SQLITE_STMTSTATUS_SORT]]
    SQLITE_STMTSTATUS_SORT
    **
    ^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.
    ** +** [[SQLITE_STMTSTATUS_AUTOINDEX]]
    SQLITE_STMTSTATUS_AUTOINDEX
    +**
    ^This is the number of rows inserted into transient indices that +** were created automatically in order to help joins run faster. +** A non-zero value in this counter may indicate an opportunity to +** improvement performance by adding permanent indices that do not +** need to be reinitialized each time the statement is run.
    **
    */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 +#define SQLITE_STMTSTATUS_AUTOINDEX 3 /* ** CAPI3REF: Custom Page Cache Object -** EXPERIMENTAL ** ** The sqlite3_pcache type is opaque. It is implemented by ** the pluggable module. The SQLite core has no knowledge of @@ -5690,43 +6809,70 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int ** sqlite3_pcache object except by holding and passing pointers ** to the object. ** -** See [sqlite3_pcache_methods] for additional information. +** See [sqlite3_pcache_methods2] for additional information. */ typedef struct sqlite3_pcache sqlite3_pcache; +/* +** CAPI3REF: Custom Page Cache Object +** +** The sqlite3_pcache_page object represents a single page in the +** page cache. The page cache will allocate instances of this +** object. Various methods of the page cache use pointers to instances +** of this object as parameters or as their return value. +** +** See [sqlite3_pcache_methods2] for additional information. +*/ +typedef struct sqlite3_pcache_page sqlite3_pcache_page; +struct sqlite3_pcache_page { + void *pBuf; /* The content of the page */ + void *pExtra; /* Extra information associated with the page */ +}; + /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} -** EXPERIMENTAL ** -** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can ** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods structure.)^ The majority of the -** heap memory used by SQLite is used by the page cache to cache data read -** from, or ready to be written to, the database file. By implementing a -** custom page cache using this API, an application can control more -** precisely the amount of memory consumed by SQLite, the way in which +** instance of the sqlite3_pcache_methods2 structure.)^ +** In many applications, most of the heap memory allocated by +** SQLite is used for the page cache. +** By implementing a +** custom page cache using this API, an application can better control +** the amount of memory consumed by SQLite, the way in which ** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** -** ^(The contents of the sqlite3_pcache_methods structure are copied to an +** The alternative page cache mechanism is an +** extreme measure that is only needed by the most demanding applications. +** The built-in page cache is recommended for most uses. +** +** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** -** ^The xInit() method is called once for each call to [sqlite3_initialize()] +** [[the xInit() page cache method]] +** ^(The xInit() method is called once for each effective +** call to [sqlite3_initialize()])^ ** (usually only once during the lifetime of the process). ^(The xInit() -** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ -** ^The xInit() method can set up up global structures and/or any mutexes +** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^ +** The intent of the xInit() method is to set up global data structures ** required by the custom page cache implementation. +** ^(If the xInit() method is NULL, then the +** built-in default page cache is used instead of the application defined +** page cache.)^ ** -** ^The xShutdown() method is called from within [sqlite3_shutdown()], -** if the application invokes this API. It can be used to clean up +** [[the xShutdown() page cache method]] +** ^The xShutdown() method is called by [sqlite3_shutdown()]. +** It can be used to clean up ** any outstanding resources before process shutdown, if required. +** ^The xShutdown() method may be NULL. ** -** ^SQLite holds a [SQLITE_MUTEX_RECURSIVE] mutex when it invokes -** the xInit method, so the xInit method need not be threadsafe. ^The +** ^SQLite automatically serializes calls to the xInit method, +** so the xInit method need not be threadsafe. ^The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. All other methods must be threadsafe ** in multithreaded applications. @@ -5734,47 +6880,59 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** -** ^The xCreate() method is used to construct a new cache instance. SQLite -** will typically create one cache instance for each open database file, +** [[the xCreate() page cache methods]] +** ^SQLite invokes the xCreate() method to construct a new cache instance. +** SQLite will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. ^szPage will not be a power of two. ^szPage -** will the page size of the database file that is to be cached plus an -** increment (here called "R") of about 100 or 200. ^SQLite will use the -** extra R bytes on each page to store metadata about the underlying -** database page on disk. The value of R depends +** be allocated by the cache. ^szPage will always a power of two. ^The +** second parameter szExtra is a number of bytes of extra storage +** associated with each page cache entry. ^The szExtra parameter will +** a number less than 250. SQLite will use the +** extra szExtra bytes on each page to store metadata about the underlying +** database page on disk. The value passed into szExtra depends ** on the SQLite version, the target platform, and how SQLite was compiled. -** ^R is constant for a particular build of SQLite. ^The second argument to -** xCreate(), bPurgeable, is true if the cache being created will -** be used to cache database pages of a file stored on disk, or -** false if it is used for an in-memory database. ^The cache implementation +** ^The third argument to xCreate(), bPurgeable, is true if the cache being +** created will be used to cache database pages of a file stored on disk, or +** false if it is used for an in-memory database. The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. -** ^In other words, a cache created with bPurgeable set to false will +** ^In other words, calls to xUnpin() on a cache with bPurgeable set to +** false will always have the "discard" flag set to true. +** ^Hence, a cache created with bPurgeable false will ** never contain any unpinned pages. ** +** [[the xCachesize() page cache method]] ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using -** the SQLite "[PRAGMA cache_size]" command.)^ ^As with the bPurgeable +** the SQLite "[PRAGMA cache_size]" command.)^ As with the bPurgeable ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** -** ^The xPagecount() method should return the number of pages currently -** stored in the cache. +** [[the xPagecount() page cache methods]] +** The xPagecount() method must return the number of pages currently +** stored in the cache, both pinned and unpinned. ** -** ^The xFetch() method is used to fetch a page and return a pointer to it. -** ^A 'page', in this context, is a buffer of szPage bytes aligned at an -** 8-byte boundary. ^The page to be fetched is determined by the key. ^The -** mimimum key value is 1. After it has been retrieved using xFetch, the page -** is considered to be "pinned". +** [[the xFetch() page cache methods]] +** The xFetch() method locates a page in the cache and returns a pointer to +** an sqlite3_pcache_page object associated with that page, or a NULL pointer. +** The pBuf element of the returned sqlite3_pcache_page object will be a +** pointer to a buffer of szPage bytes used to store the content of a +** single database page. The pExtra element of sqlite3_pcache_page will be +** a pointer to the szExtra bytes of extra storage that SQLite has requested +** for each entry in the page cache. ** -** ^If the requested page is already in the page cache, then the page cache +** The page to be fetched is determined by the key. ^The minimum key value +** is 1. After it has been retrieved using xFetch, the page is considered +** to be "pinned". +** +** If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content -** intact. ^(If the requested page is not already in the cache, then the -** behavior of the cache implementation is determined by the value of the -** createFlag parameter passed to xFetch, according to the following table: +** intact. If the requested page is not already in the cache, then the +** cache implementation should use the value of the createFlag +** parameter to help it determined what action to take: ** ** **
    createFlag Behaviour when page is not already in cache @@ -5783,44 +6941,75 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** Otherwise return NULL. **
    2 Make every effort to allocate a new page. Only return ** NULL if allocating a new page is effectively impossible. -**
    )^ +** ** -** SQLite will normally invoke xFetch() with a createFlag of 0 or 1. If -** a call to xFetch() with createFlag==1 returns NULL, then SQLite will +** ^(SQLite will normally invoke xFetch() with a createFlag of 0 or 1. SQLite +** will only use a createFlag of 2 after a prior call with a createFlag of 1 +** failed.)^ In between the to xFetch() calls, SQLite may ** attempt to unpin one or more cache pages by spilling the content of -** pinned pages to disk and synching the operating system disk cache. After -** attempting to unpin pages, the xFetch() method will be invoked again with -** a createFlag of 2. +** pinned pages to disk and synching the operating system disk cache. ** +** [[the xUnpin() page cache method]] ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page -** as its second argument. ^(If the third parameter, discard, is non-zero, -** then the page should be evicted from the cache. In this case SQLite -** assumes that the next time the page is retrieved from the cache using -** the xFetch() method, it will be zeroed.)^ ^If the discard parameter is -** zero, then the page is considered to be unpinned. ^The cache implementation +** as its second argument. If the third parameter, discard, is non-zero, +** then the page must be evicted from the cache. +** ^If the discard parameter is +** zero, then the page may be discarded or retained at the discretion of +** page cache implementation. ^The page cache implementation ** may choose to evict unpinned pages at any time. ** -** ^(The cache is not required to perform any reference counting. A single +** The cache must not perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls -** to xFetch().)^ +** to xFetch(). ** -** ^The xRekey() method is used to change the key value associated with the -** page passed as the second argument from oldKey to newKey. ^If the cache -** previously contains an entry associated with newKey, it should be +** [[the xRekey() page cache methods]] +** The xRekey() method is used to change the key value associated with the +** page passed as the second argument. If the cache +** previously contains an entry associated with newKey, it must be ** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** -** ^When SQLite calls the xTruncate() method, the cache must discard all +** When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal -** to the value of the iLimit parameter passed to xTruncate(). ^If any +** to the value of the iLimit parameter passed to xTruncate(). If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** +** [[the xDestroy() page cache method]] ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] -** handle invalid, and will not use it with any other sqlite3_pcache_methods +** handle invalid, and will not use it with any other sqlite3_pcache_methods2 ** functions. +** +** [[the xShrink() page cache method]] +** ^SQLite invokes the xShrink() method when it wants the page cache to +** free up as much of heap memory as possible. The page cache implementation +** is not obligated to free any memory, but well-behaved implementations should +** do their best. +*/ +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; +struct sqlite3_pcache_methods2 { + int iVersion; + void *pArg; + int (*xInit)(void*); + void (*xShutdown)(void*); + sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); + void (*xCachesize)(sqlite3_pcache*, int nCachesize); + int (*xPagecount)(sqlite3_pcache*); + sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); + void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); + void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, + unsigned oldKey, unsigned newKey); + void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); + void (*xDestroy)(sqlite3_pcache*); + void (*xShrink)(sqlite3_pcache*); +}; + +/* +** This is the obsolete pcache_methods object that has now been replaced +** by sqlite3_pcache_methods2. This object is not used by SQLite. It is +** retained in the header file for backwards compatibility only. */ typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; struct sqlite3_pcache_methods { @@ -5837,9 +7026,9 @@ struct sqlite3_pcache_methods { void (*xDestroy)(sqlite3_pcache*); }; + /* ** CAPI3REF: Online Backup Object -** EXPERIMENTAL ** ** The sqlite3_backup object records state information about an ongoing ** online backup operation. ^The sqlite3_backup object is created by @@ -5852,7 +7041,6 @@ typedef struct sqlite3_backup sqlite3_backup; /* ** CAPI3REF: Online Backup API. -** EXPERIMENTAL ** ** The backup API copies the content of one database into another. ** It is useful either for creating backups of databases or @@ -5860,11 +7048,12 @@ typedef struct sqlite3_backup sqlite3_backup; ** ** See Also: [Using the SQLite Online Backup API] ** -** ^Exclusive access is required to the destination database for the -** duration of the operation. ^However the source database is only -** read-locked while it is actually being read; it is not locked -** continuously for the entire backup operation. ^Thus, the backup may be -** performed on a live source database without preventing other users from +** ^SQLite holds a write transaction open on the destination database file +** for the duration of the backup operation. +** ^The source database is read-locked only while it is being read; +** it is not locked continuously for the entire backup operation. +** ^Thus, the backup may be performed on a live source database without +** preventing other database connections from ** reading or writing to the source database while the backup is underway. ** ** ^(To perform a backup operation: @@ -5879,7 +7068,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** There should be exactly one call to sqlite3_backup_finish() for each ** successful call to sqlite3_backup_init(). ** -** sqlite3_backup_init() +** [[sqlite3_backup_init()]] sqlite3_backup_init() ** ** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the ** [database connection] associated with the destination database @@ -5891,11 +7080,11 @@ typedef struct sqlite3_backup sqlite3_backup; ** sqlite3_backup_init(D,N,S,M) identify the [database connection] ** and database name of the source database, respectively. ** ^The source and destination [database connections] (parameters S and D) -** must be different or else sqlite3_backup_init(D,N,S,M) will file with +** must be different or else sqlite3_backup_init(D,N,S,M) will fail with ** an error. ** ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is -** returned and an error code and error message are store3d in the +** returned and an error code and error message are stored in the ** destination [database connection] D. ** ^The error code and message for the failed call to sqlite3_backup_init() ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or @@ -5906,13 +7095,13 @@ typedef struct sqlite3_backup sqlite3_backup; ** sqlite3_backup_finish() functions to perform the specified backup ** operation. ** -** sqlite3_backup_step() +** [[sqlite3_backup_step()]] sqlite3_backup_step() ** ** ^Function sqlite3_backup_step(B,N) will copy up to N pages between ** the source and destination databases specified by [sqlite3_backup] object B. ** ^If N is negative, all remaining source pages are copied. ** ^If sqlite3_backup_step(B,N) successfully copies N pages and there -** are still more pages to be copied, then the function resturns [SQLITE_OK]. +** are still more pages to be copied, then the function returns [SQLITE_OK]. ** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages ** from source to destination, then it returns [SQLITE_DONE]. ** ^If an error occurs while running sqlite3_backup_step(B,N), @@ -5921,10 +7110,14 @@ typedef struct sqlite3_backup sqlite3_backup; ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. ** -** ^The sqlite3_backup_step() might return [SQLITE_READONLY] if the destination -** database was opened read-only or if -** the destination is an in-memory database with a different page size -** from the source database. +** ^(The sqlite3_backup_step() might return [SQLITE_READONLY] if +**
      +**
    1. the destination database was opened read-only, or +**
    2. the destination database is using write-ahead-log journaling +** and the destination and source page sizes differ, or +**
    3. the destination database is an in-memory database and the +** destination and source page sizes differ. +**
    )^ ** ** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then ** the [sqlite3_busy_handler | busy-handler function] @@ -5959,7 +7152,7 @@ typedef struct sqlite3_backup sqlite3_backup; ** by the backup operation, then the backup database is automatically ** updated at the same time. ** -** sqlite3_backup_finish() +** [[sqlite3_backup_finish()]] sqlite3_backup_finish() ** ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the ** application wishes to abandon the backup operation, the application @@ -5982,11 +7175,12 @@ typedef struct sqlite3_backup sqlite3_backup; ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** -** sqlite3_backup_remaining(), sqlite3_backup_pagecount() +** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]] +** sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed -** up and the total number of pages in the source databae file. +** up and the total number of pages in the source database file. ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces ** retrieve these two values, respectively. ** @@ -6040,7 +7234,6 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification -** EXPERIMENTAL ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or @@ -6083,7 +7276,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is cancelled. ^The blocked connections +** unlock-notify callback is canceled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** @@ -6162,15 +7355,302 @@ SQLITE_API int sqlite3_unlock_notify( /* ** CAPI3REF: String Comparison -** EXPERIMENTAL ** -** ^The [sqlite3_strnicmp()] API allows applications and extensions to -** compare the contents of two buffers containing UTF-8 strings in a -** case-indendent fashion, using the same definition of case independence -** that SQLite uses internally when comparing identifiers. +** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications +** and extensions to compare the contents of two buffers containing UTF-8 +** strings in a case-independent fashion, using the same definition of "case +** independence" that SQLite uses internally when comparing identifiers. */ +SQLITE_API int sqlite3_stricmp(const char *, const char *); SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); +/* +** CAPI3REF: Error Logging Interface +** +** ^The [sqlite3_log()] interface writes a message into the error log +** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. +** ^If logging is enabled, the zFormat string and subsequent arguments are +** used with [sqlite3_snprintf()] to generate the final output string. +** +** The sqlite3_log() interface is intended for use by extensions such as +** virtual tables, collating functions, and SQL functions. While there is +** nothing to prevent an application from calling sqlite3_log(), doing so +** is considered bad form. +** +** The zFormat string must not be NULL. +** +** To avoid deadlocks and other threading problems, the sqlite3_log() routine +** will not use dynamically allocated memory. The log message is stored in +** a fixed-length buffer on the stack. If the log message is longer than +** a few hundred characters, it will be truncated to the length of the +** buffer. +*/ +SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); + +/* +** CAPI3REF: Write-Ahead Log Commit Hook +** +** ^The [sqlite3_wal_hook()] function is used to register a callback that +** will be invoked each time a database connection commits data to a +** [write-ahead log] (i.e. whenever a transaction is committed in +** [journal_mode | journal_mode=WAL mode]). +** +** ^The callback is invoked by SQLite after the commit has taken place and +** the associated write-lock on the database released, so the implementation +** may read, write or [checkpoint] the database as required. +** +** ^The first parameter passed to the callback function when it is invoked +** is a copy of the third parameter passed to sqlite3_wal_hook() when +** registering the callback. ^The second is a copy of the database handle. +** ^The third parameter is the name of the database that was written to - +** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter +** is the number of pages currently in the write-ahead log file, +** including those that were just committed. +** +** The callback function should normally return [SQLITE_OK]. ^If an error +** code is returned, that error will propagate back up through the +** SQLite code base to cause the statement that provoked the callback +** to report an error, though the commit will have still occurred. If the +** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value +** that does not correspond to any valid SQLite error code, the results +** are undefined. +** +** A single database handle may have at most a single write-ahead log callback +** registered at one time. ^Calling [sqlite3_wal_hook()] replaces any +** previously registered write-ahead log callback. ^Note that the +** [sqlite3_wal_autocheckpoint()] interface and the +** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will +** those overwrite any prior [sqlite3_wal_hook()] settings. +*/ +SQLITE_API void *sqlite3_wal_hook( + sqlite3*, + int(*)(void *,sqlite3*,const char*,int), + void* +); + +/* +** CAPI3REF: Configure an auto-checkpoint +** +** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around +** [sqlite3_wal_hook()] that causes any database on [database connection] D +** to automatically [checkpoint] +** after committing a transaction if there are N or +** more frames in the [write-ahead log] file. ^Passing zero or +** a negative value as the nFrame parameter disables automatic +** checkpoints entirely. +** +** ^The callback registered by this function replaces any existing callback +** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback +** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism +** configured by this function. +** +** ^The [wal_autocheckpoint pragma] can be used to invoke this interface +** from SQL. +** +** ^Every new [database connection] defaults to having the auto-checkpoint +** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] +** pages. The use of this interface +** is only necessary if the default setting is found to be suboptimal +** for a particular application. +*/ +SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); + +/* +** CAPI3REF: Checkpoint a database +** +** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X +** on [database connection] D to be [checkpointed]. ^If X is NULL or an +** empty string, then a checkpoint is run on all databases of +** connection D. ^If the database connection D is not in +** [WAL | write-ahead log mode] then this interface is a harmless no-op. +** +** ^The [wal_checkpoint pragma] can be used to invoke this interface +** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the +** [wal_autocheckpoint pragma] can be used to cause this interface to be +** run whenever the WAL reaches a certain size threshold. +** +** See also: [sqlite3_wal_checkpoint_v2()] +*/ +SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb); + +/* +** CAPI3REF: Checkpoint a database +** +** Run a checkpoint operation on WAL database zDb attached to database +** handle db. The specific operation is determined by the value of the +** eMode parameter: +** +**
    +**
    SQLITE_CHECKPOINT_PASSIVE
    +** Checkpoint as many frames as possible without waiting for any database +** readers or writers to finish. Sync the db file if all frames in the log +** are checkpointed. This mode is the same as calling +** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked. +** +**
    SQLITE_CHECKPOINT_FULL
    +** This mode blocks (calls the busy-handler callback) until there is no +** database writer and all readers are reading from the most recent database +** snapshot. It then checkpoints all frames in the log file and syncs the +** database file. This call blocks database writers while it is running, +** but not database readers. +** +**
    SQLITE_CHECKPOINT_RESTART
    +** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after +** checkpointing the log file it blocks (calls the busy-handler callback) +** until all readers are reading from the database file only. This ensures +** that the next client to write to the database file restarts the log file +** from the beginning. This call blocks database writers while it is running, +** but not database readers. +**
    +** +** If pnLog is not NULL, then *pnLog is set to the total number of frames in +** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to +** the total number of checkpointed frames (including any that were already +** checkpointed when this function is called). *pnLog and *pnCkpt may be +** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK. +** If no values are available because of an error, they are both set to -1 +** before returning to communicate this to the caller. +** +** All calls obtain an exclusive "checkpoint" lock on the database file. If +** any other process is running a checkpoint operation at the same time, the +** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a +** busy-handler configured, it will not be invoked in this case. +** +** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive +** "writer" lock on the database file. If the writer lock cannot be obtained +** immediately, and a busy-handler is configured, it is invoked and the writer +** lock retried until either the busy-handler returns 0 or the lock is +** successfully obtained. The busy-handler is also invoked while waiting for +** database readers as described above. If the busy-handler returns 0 before +** the writer lock is obtained or while waiting for database readers, the +** checkpoint operation proceeds from that point in the same way as +** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible +** without blocking any further. SQLITE_BUSY is returned in this case. +** +** If parameter zDb is NULL or points to a zero length string, then the +** specified operation is attempted on all WAL databases. In this case the +** values written to output parameters *pnLog and *pnCkpt are undefined. If +** an SQLITE_BUSY error is encountered when processing one or more of the +** attached WAL databases, the operation is still attempted on any remaining +** attached databases and SQLITE_BUSY is returned to the caller. If any other +** error occurs while processing an attached database, processing is abandoned +** and the error code returned to the caller immediately. If no error +** (SQLITE_BUSY or otherwise) is encountered while processing the attached +** databases, SQLITE_OK is returned. +** +** If database zDb is the name of an attached database that is not in WAL +** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If +** zDb is not NULL (or a zero length string) and is not the name of any +** attached database, SQLITE_ERROR is returned to the caller. +*/ +SQLITE_API int sqlite3_wal_checkpoint_v2( + sqlite3 *db, /* Database handle */ + const char *zDb, /* Name of attached database (or NULL) */ + int eMode, /* SQLITE_CHECKPOINT_* value */ + int *pnLog, /* OUT: Size of WAL log in frames */ + int *pnCkpt /* OUT: Total number of frames checkpointed */ +); + +/* +** CAPI3REF: Checkpoint operation parameters +** +** These constants can be used as the 3rd parameter to +** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()] +** documentation for additional information about the meaning and use of +** each of these values. +*/ +#define SQLITE_CHECKPOINT_PASSIVE 0 +#define SQLITE_CHECKPOINT_FULL 1 +#define SQLITE_CHECKPOINT_RESTART 2 + +/* +** CAPI3REF: Virtual Table Interface Configuration +** +** This function may be called by either the [xConnect] or [xCreate] method +** of a [virtual table] implementation to configure +** various facets of the virtual table interface. +** +** If this interface is invoked outside the context of an xConnect or +** xCreate virtual table method then the behavior is undefined. +** +** At present, there is only one option that may be configured using +** this function. (See [SQLITE_VTAB_CONSTRAINT_SUPPORT].) Further options +** may be added in the future. +*/ +SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...); + +/* +** CAPI3REF: Virtual Table Configuration Options +** +** These macros define the various options to the +** [sqlite3_vtab_config()] interface that [virtual table] implementations +** can use to customize and optimize their behavior. +** +**
    +**
    SQLITE_VTAB_CONSTRAINT_SUPPORT +**
    Calls of the form +** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported, +** where X is an integer. If X is zero, then the [virtual table] whose +** [xCreate] or [xConnect] method invoked [sqlite3_vtab_config()] does not +** support constraints. In this configuration (which is the default) if +** a call to the [xUpdate] method returns [SQLITE_CONSTRAINT], then the entire +** statement is rolled back as if [ON CONFLICT | OR ABORT] had been +** specified as part of the users SQL statement, regardless of the actual +** ON CONFLICT mode specified. +** +** If X is non-zero, then the virtual table implementation guarantees +** that if [xUpdate] returns [SQLITE_CONSTRAINT], it will do so before +** any modifications to internal or persistent data structures have been made. +** If the [ON CONFLICT] mode is ABORT, FAIL, IGNORE or ROLLBACK, SQLite +** is able to roll back a statement or database transaction, and abandon +** or continue processing the current SQL statement as appropriate. +** If the ON CONFLICT mode is REPLACE and the [xUpdate] method returns +** [SQLITE_CONSTRAINT], SQLite handles this as if the ON CONFLICT mode +** had been ABORT. +** +** Virtual table implementations that are required to handle OR REPLACE +** must do so within the [xUpdate] method. If a call to the +** [sqlite3_vtab_on_conflict()] function indicates that the current ON +** CONFLICT policy is REPLACE, the virtual table implementation should +** silently replace the appropriate rows within the xUpdate callback and +** return SQLITE_OK. Or, if this is not possible, it may return +** SQLITE_CONSTRAINT, in which case SQLite falls back to OR ABORT +** constraint handling. +**
    +*/ +#define SQLITE_VTAB_CONSTRAINT_SUPPORT 1 + +/* +** CAPI3REF: Determine The Virtual Table Conflict Policy +** +** This function may only be called from within a call to the [xUpdate] method +** of a [virtual table] implementation for an INSERT or UPDATE operation. ^The +** value returned is one of [SQLITE_ROLLBACK], [SQLITE_IGNORE], [SQLITE_FAIL], +** [SQLITE_ABORT], or [SQLITE_REPLACE], according to the [ON CONFLICT] mode +** of the SQL statement that triggered the call to the [xUpdate] method of the +** [virtual table]. +*/ +SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *); + +/* +** CAPI3REF: Conflict resolution modes +** +** These constants are returned by [sqlite3_vtab_on_conflict()] to +** inform a [virtual table] implementation what the [ON CONFLICT] mode +** is for the SQL statement being evaluated. +** +** Note that the [SQLITE_IGNORE] constant is also used as a potential +** return value from the [sqlite3_set_authorizer()] callback and that +** [SQLITE_ABORT] is also a [result code]. +*/ +#define SQLITE_ROLLBACK 1 +/* #define SQLITE_IGNORE 2 // Also used by sqlite3_authorizer() callback */ +#define SQLITE_FAIL 3 +/* #define SQLITE_ABORT 4 // Also an error code */ +#define SQLITE_REPLACE 5 + + + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. @@ -6184,6 +7664,66 @@ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); #endif #endif +/* +** 2010 August 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +*/ + +#ifndef _SQLITE3RTREE_H_ +#define _SQLITE3RTREE_H_ + + +#if 0 +extern "C" { +#endif + +typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry; + +/* +** Register a geometry callback named zGeom that can be used as part of an +** R-Tree geometry query as follows: +** +** SELECT ... FROM WHERE MATCH $zGeom(... params ...) +*/ +SQLITE_API int sqlite3_rtree_geometry_callback( + sqlite3 *db, + const char *zGeom, +#ifdef SQLITE_RTREE_INT_ONLY + int (*xGeom)(sqlite3_rtree_geometry*, int n, sqlite3_int64 *a, int *pRes), +#else + int (*xGeom)(sqlite3_rtree_geometry*, int n, double *a, int *pRes), +#endif + void *pContext +); + + +/* +** A pointer to a structure of the following type is passed as the first +** argument to callbacks registered using rtree_geometry_callback(). +*/ +struct sqlite3_rtree_geometry { + void *pContext; /* Copy of pContext passed to s_r_g_c() */ + int nParam; /* Size of array aParam[] */ + double *aParam; /* Parameters passed to SQL geom function */ + void *pUser; /* Callback implementation user data */ + void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */ +}; + + +#if 0 +} /* end of the 'extern "C"' block */ +#endif + +#endif /* ifndef _SQLITE3RTREE_H_ */ + /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -6462,6 +8002,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 +# define float sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) @@ -6486,20 +8027,6 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); #define OMIT_TEMPDB 0 #endif -/* -** If the following macro is set to 1, then NULL values are considered -** distinct when determining whether or not two entries are the same -** in a UNIQUE index. This is the way PostgreSQL, Oracle, DB2, MySQL, -** OCELOT, and Firebird all work. The SQL92 spec explicitly says this -** is the way things are suppose to work. -** -** If the following macro is set to 0, the NULLs are indistinct for -** a UNIQUE index. In this mode, you can only have a single NULL entry -** for a column declared UNIQUE. This is the way Informix and SQL Server -** work. -*/ -#define NULL_DISTINCT_FOR_UNIQUE 1 - /* ** The "file format" number is an integer that is incremented whenever ** the VDBE-level file format changes. The following macros define the @@ -6508,9 +8035,13 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*); */ #define SQLITE_MAX_FILE_FORMAT 4 #ifndef SQLITE_DEFAULT_FILE_FORMAT -# define SQLITE_DEFAULT_FILE_FORMAT 1 +# define SQLITE_DEFAULT_FILE_FORMAT 4 #endif +/* +** Determine whether triggers are recursive by default. This can be +** changed at run-time using a pragma. +*/ #ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS # define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 #endif @@ -6602,6 +8133,18 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */ */ #define SQLITE_MAX_U32 ((((u64)1)<<32)-1) +/* +** The datatype used to store estimates of the number of rows in a +** table or index. This is an unsigned integer type. For 99.9% of +** the world, a 32-bit integer is sufficient. But a 64-bit integer +** can be used at compile-time if desired. +*/ +#ifdef SQLITE_64BIT_STATS + typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */ +#else + typedef u32 tRowcnt; /* 32-bit is the default */ +#endif + /* ** Macros to determine whether the machine is big or little endian, ** evaluated at runtime. @@ -6699,9 +8242,13 @@ struct BusyHandler { /* ** The following value as a destructor means to use sqlite3DbFree(). -** This is an internal extension to SQLITE_STATIC and SQLITE_TRANSIENT. +** The sqlite3DbFree() routine requires two parameters instead of the +** one parameter that destructors normally want. So we have to introduce +** this magic value that the code knows to handle differently. Any +** pointer will work here as long as it is distinct from SQLITE_STATIC +** and SQLITE_TRANSIENT. */ -#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3DbFree) +#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize) /* ** When SQLITE_OMIT_WSD is defined, it means that the target platform does @@ -6755,7 +8302,6 @@ typedef struct AggInfo AggInfo; typedef struct AuthContext AuthContext; typedef struct AutoincInfo AutoincInfo; typedef struct Bitvec Bitvec; -typedef struct RowSet RowSet; typedef struct CollSeq CollSeq; typedef struct Column Column; typedef struct Db Db; @@ -6764,6 +8310,7 @@ typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; +typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; @@ -6776,18 +8323,21 @@ typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; typedef struct Parse Parse; +typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; +typedef struct SelectDest SelectDest; typedef struct SrcList SrcList; typedef struct StrAccum StrAccum; typedef struct Table Table; typedef struct TableLock TableLock; typedef struct Token Token; +typedef struct Trigger Trigger; typedef struct TriggerPrg TriggerPrg; typedef struct TriggerStep TriggerStep; -typedef struct Trigger Trigger; typedef struct UnpackedRecord UnpackedRecord; typedef struct VTable VTable; +typedef struct VtabCtx VtabCtx; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; @@ -6841,21 +8391,10 @@ typedef struct WhereLevel WhereLevel; typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; -typedef struct BtreeMutexArray BtreeMutexArray; - -/* -** This structure records all of the Btrees that need to hold -** a mutex before we enter sqlite3VdbeExec(). The Btrees are -** are placed in aBtree[] in order of aBtree[]->pBt. That way, -** we can always lock and unlock them all quickly. -*/ -struct BtreeMutexArray { - int nMutex; - Btree *aBtree[SQLITE_MAX_ATTACHED+1]; -}; SQLITE_PRIVATE int sqlite3BtreeOpen( + sqlite3_vfs *pVfs, /* VFS to use with this b-tree */ const char *zFilename, /* Name of database file to open */ sqlite3 *db, /* Associated database connection */ Btree **ppBtree, /* Return open Btree* here */ @@ -6869,28 +8408,31 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ -#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ -#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ -#define BTREE_MEMORY 4 /* In-memory DB. No argument */ -#define BTREE_READONLY 8 /* Open the database in read-only mode */ -#define BTREE_READWRITE 16 /* Open for both reading and writing */ -#define BTREE_CREATE 32 /* Create the database if it does not exist */ +#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */ +#define BTREE_MEMORY 2 /* This is an in-memory DB */ +#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */ +#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */ SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); -SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int); +SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int); SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); +SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*); +SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); +#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_DEBUG) +SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p); +#endif SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); -SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*); +SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int); SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); -SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*); +SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); @@ -6908,11 +8450,17 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR -** of the following flags: +** of the flags shown below. +** +** Every SQLite table must have either BTREE_INTKEY or BTREE_BLOBKEY set. +** With BTREE_INTKEY, the table key is a 64-bit integer and arbitrary data +** is stored in the leaves. (BTREE_INTKEY is used for SQL tables.) With +** BTREE_BLOBKEY, the key is an arbitrary BLOB and no content is stored +** anywhere - the key is the content. (BTREE_BLOBKEY is used for SQL +** indices.) */ #define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ -#define BTREE_ZERODATA 2 /* Table has keys only - no data */ -#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ +#define BTREE_BLOBKEY 2 /* Table has keys only - no data */ SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); @@ -6921,6 +8469,8 @@ SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); +SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p); + /* ** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta ** should be one of the following values. The integer values are assigned @@ -6942,6 +8492,12 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); #define BTREE_USER_VERSION 6 #define BTREE_INCR_VACUUM 7 +/* +** Values that may be OR'd together to form the second argument of an +** sqlite3BtreeCursorHints() call. +*/ +#define BTREE_BULKLOAD 0x00000001 + SQLITE_PRIVATE int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ int iTable, /* Index of root page */ @@ -6985,6 +8541,8 @@ SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); +SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion); +SQLITE_PRIVATE void sqlite3BtreeCursorHints(BtCursor *, unsigned int mask); #ifndef NDEBUG SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); @@ -6999,6 +8557,10 @@ SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); #endif +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *); +#endif + /* ** If we are not using shared cache, then there is no need to ** use mutexes to access the BtShared structures. So make the @@ -7013,30 +8575,28 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE +SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*); SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); -SQLITE_PRIVATE void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); -SQLITE_PRIVATE void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); -SQLITE_PRIVATE void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); #ifndef NDEBUG /* These routines are used inside assert() statements only. */ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); +SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); #endif #else +# define sqlite3BtreeSharable(X) 0 # define sqlite3BtreeLeave(X) # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) -# define sqlite3BtreeMutexArrayEnter(X) -# define sqlite3BtreeMutexArrayLeave(X) -# define sqlite3BtreeMutexArrayInsert(X,Y) # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3SchemaMutexHeld(X,Y,Z) 1 #endif @@ -7065,6 +8625,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ +/* #include */ /* ** A single VDBE is an opaque structure named "Vdbe". Only routines @@ -7108,6 +8669,7 @@ struct VdbeOp { KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ + int (*xAdvance)(BtCursor *, int *); } p4; #ifdef SQLITE_DEBUG char *zComment; /* Comment to improve readability */ @@ -7128,8 +8690,9 @@ struct SubProgram { int nOp; /* Elements in aOp[] */ int nMem; /* Number of memory cells required */ int nCsr; /* Number of cursors required */ - int nRef; /* Number of pointers to this structure */ + int nOnce; /* Number of OP_Once instructions */ void *token; /* id that may be used to recursive triggers */ + SubProgram *pNext; /* Next sub-program already visited */ }; /* @@ -7155,7 +8718,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ #define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ #define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ -#define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */ +#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */ #define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ @@ -7163,6 +8726,7 @@ typedef struct VdbeOpList VdbeOpList; #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ +#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the @@ -7260,102 +8824,105 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Or 68 /* same as TK_OR */ #define OP_Not 19 /* same as TK_NOT */ #define OP_BitNot 93 /* same as TK_BITNOT */ -#define OP_If 26 -#define OP_IfNot 27 +#define OP_Once 26 +#define OP_If 27 +#define OP_IfNot 28 #define OP_IsNull 73 /* same as TK_ISNULL */ #define OP_NotNull 74 /* same as TK_NOTNULL */ -#define OP_Column 28 -#define OP_Affinity 29 -#define OP_MakeRecord 30 -#define OP_Count 31 -#define OP_Savepoint 32 -#define OP_AutoCommit 33 -#define OP_Transaction 34 -#define OP_ReadCookie 35 -#define OP_SetCookie 36 -#define OP_VerifyCookie 37 -#define OP_OpenRead 38 -#define OP_OpenWrite 39 -#define OP_OpenEphemeral 40 -#define OP_OpenPseudo 41 -#define OP_Close 42 -#define OP_SeekLt 43 -#define OP_SeekLe 44 -#define OP_SeekGe 45 -#define OP_SeekGt 46 -#define OP_Seek 47 -#define OP_NotFound 48 -#define OP_Found 49 -#define OP_IsUnique 50 -#define OP_NotExists 51 -#define OP_Sequence 52 -#define OP_NewRowid 53 -#define OP_Insert 54 -#define OP_InsertInt 55 -#define OP_Delete 56 -#define OP_ResetCount 57 -#define OP_RowKey 58 -#define OP_RowData 59 -#define OP_Rowid 60 -#define OP_NullRow 61 -#define OP_Last 62 -#define OP_Sort 63 -#define OP_Rewind 64 -#define OP_Prev 65 -#define OP_Next 66 -#define OP_IdxInsert 67 -#define OP_IdxDelete 70 -#define OP_IdxRowid 71 -#define OP_IdxLT 72 -#define OP_IdxGE 81 -#define OP_Destroy 92 -#define OP_Clear 95 -#define OP_CreateIndex 96 -#define OP_CreateTable 97 -#define OP_ParseSchema 98 -#define OP_LoadAnalysis 99 -#define OP_DropTable 100 -#define OP_DropIndex 101 -#define OP_DropTrigger 102 -#define OP_IntegrityCk 103 -#define OP_RowSetAdd 104 -#define OP_RowSetRead 105 -#define OP_RowSetTest 106 -#define OP_Program 107 -#define OP_Param 108 -#define OP_FkCounter 109 -#define OP_FkIfZero 110 -#define OP_MemMax 111 -#define OP_IfPos 112 -#define OP_IfNeg 113 -#define OP_IfZero 114 -#define OP_AggStep 115 -#define OP_AggFinal 116 -#define OP_Vacuum 117 -#define OP_IncrVacuum 118 -#define OP_Expire 119 -#define OP_TableLock 120 -#define OP_VBegin 121 -#define OP_VCreate 122 -#define OP_VDestroy 123 -#define OP_VOpen 124 -#define OP_VFilter 125 -#define OP_VColumn 126 -#define OP_VNext 127 -#define OP_VRename 128 -#define OP_VUpdate 129 -#define OP_Pagecount 131 -#define OP_Trace 132 -#define OP_Noop 133 -#define OP_Explain 134 - -/* The following opcode values are never used */ -#define OP_NotUsed_135 135 -#define OP_NotUsed_136 136 -#define OP_NotUsed_137 137 -#define OP_NotUsed_138 138 -#define OP_NotUsed_139 139 -#define OP_NotUsed_140 140 +#define OP_Column 29 +#define OP_Affinity 30 +#define OP_MakeRecord 31 +#define OP_Count 32 +#define OP_Savepoint 33 +#define OP_AutoCommit 34 +#define OP_Transaction 35 +#define OP_ReadCookie 36 +#define OP_SetCookie 37 +#define OP_VerifyCookie 38 +#define OP_OpenRead 39 +#define OP_OpenWrite 40 +#define OP_OpenAutoindex 41 +#define OP_OpenEphemeral 42 +#define OP_SorterOpen 43 +#define OP_OpenPseudo 44 +#define OP_Close 45 +#define OP_SeekLt 46 +#define OP_SeekLe 47 +#define OP_SeekGe 48 +#define OP_SeekGt 49 +#define OP_Seek 50 +#define OP_NotFound 51 +#define OP_Found 52 +#define OP_IsUnique 53 +#define OP_NotExists 54 +#define OP_Sequence 55 +#define OP_NewRowid 56 +#define OP_Insert 57 +#define OP_InsertInt 58 +#define OP_Delete 59 +#define OP_ResetCount 60 +#define OP_SorterCompare 61 +#define OP_SorterData 62 +#define OP_RowKey 63 +#define OP_RowData 64 +#define OP_Rowid 65 +#define OP_NullRow 66 +#define OP_Last 67 +#define OP_SorterSort 70 +#define OP_Sort 71 +#define OP_Rewind 72 +#define OP_SorterNext 81 +#define OP_Prev 92 +#define OP_Next 95 +#define OP_SorterInsert 96 +#define OP_IdxInsert 97 +#define OP_IdxDelete 98 +#define OP_IdxRowid 99 +#define OP_IdxLT 100 +#define OP_IdxGE 101 +#define OP_Destroy 102 +#define OP_Clear 103 +#define OP_CreateIndex 104 +#define OP_CreateTable 105 +#define OP_ParseSchema 106 +#define OP_LoadAnalysis 107 +#define OP_DropTable 108 +#define OP_DropIndex 109 +#define OP_DropTrigger 110 +#define OP_IntegrityCk 111 +#define OP_RowSetAdd 112 +#define OP_RowSetRead 113 +#define OP_RowSetTest 114 +#define OP_Program 115 +#define OP_Param 116 +#define OP_FkCounter 117 +#define OP_FkIfZero 118 +#define OP_MemMax 119 +#define OP_IfPos 120 +#define OP_IfNeg 121 +#define OP_IfZero 122 +#define OP_AggStep 123 +#define OP_AggFinal 124 +#define OP_Checkpoint 125 +#define OP_JournalMode 126 +#define OP_Vacuum 127 +#define OP_IncrVacuum 128 +#define OP_Expire 129 +#define OP_TableLock 131 +#define OP_VBegin 132 +#define OP_VCreate 133 +#define OP_VDestroy 134 +#define OP_VOpen 135 +#define OP_VFilter 136 +#define OP_VColumn 137 +#define OP_VNext 138 +#define OP_VRename 139 +#define OP_VUpdate 140 +#define OP_Pagecount 146 +#define OP_MaxPgcnt 147 +#define OP_Trace 148 +#define OP_Noop 149 +#define OP_Explain 150 /* Properties such as "out2" or "jump" that are specified in @@ -7370,25 +8937,25 @@ typedef struct VdbeOpList VdbeOpList; #define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ #define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,\ -/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24, 0x24,\ +/* 0 */ 0x00, 0x01, 0x01, 0x04, 0x04, 0x10, 0x00, 0x02,\ +/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24,\ /* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ -/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,\ -/* 32 */ 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,\ -/* 40 */ 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x08,\ -/* 48 */ 0x11, 0x11, 0x11, 0x11, 0x02, 0x02, 0x00, 0x00,\ -/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01,\ -/* 64 */ 0x01, 0x01, 0x01, 0x08, 0x4c, 0x4c, 0x00, 0x02,\ +/* 24 */ 0x00, 0x01, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00,\ +/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\ +/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\ +/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\ +/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\ /* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ /* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ -/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x02, 0x24, 0x02, 0x00,\ -/* 96 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 104 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ -/* 112 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,\ -/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,\ -/* 128 */ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,\ -/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\ -/* 144 */ 0x04, 0x04,} +/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\ +/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\ +/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ +/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\ +/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\ +/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,} /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ @@ -7405,18 +8972,21 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); -SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); -SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); -SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); +SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); +SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1); +SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2); +SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); -SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); +SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr); SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); +SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*); SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); @@ -7425,6 +8995,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); #endif SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); +SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*); SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); @@ -7433,16 +9004,19 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); -SQLITE_PRIVATE void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int); SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); #ifndef SQLITE_OMIT_TRACE SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif -SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int); -SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); +SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); +SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); + +#ifndef SQLITE_OMIT_TRIGGER +SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); +#endif #ifndef NDEBUG @@ -7521,7 +9095,7 @@ typedef struct PgHdr DbPage; ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ -#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ +#define PAGER_MEMORY 0x0002 /* In-memory database */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). @@ -7531,14 +9105,15 @@ typedef struct PgHdr DbPage; #define PAGER_LOCKINGMODE_EXCLUSIVE 1 /* -** Valid values for the second argument to sqlite3PagerJournalMode(). +** Numeric constants that encode the journalmode. */ -#define PAGER_JOURNALMODE_QUERY -1 +#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */ #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ +#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ /* ** The remainder of this file contains the declarations of the functions @@ -7561,12 +9136,15 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); -SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*, int); +SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); -SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int); +SQLITE_PRIVATE void sqlite3PagerShrink(Pager*); +SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int); SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); -SQLITE_PRIVATE int sqlite3PagerJournalMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int); +SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*); +SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*); SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); @@ -7586,9 +9164,10 @@ SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); /* Functions used to manage pager transactions and savepoints. */ -SQLITE_PRIVATE int sqlite3PagerPagecount(Pager*, int*); +SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*); SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); +SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*); SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); @@ -7596,20 +9175,40 @@ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); +#ifndef SQLITE_OMIT_WAL +SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*); +SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager); +SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); +SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager); +#endif + +#ifdef SQLITE_ENABLE_ZIPVFS +SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager); +#endif + /* Functions used to query pager state and configuration. */ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); -SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*); +SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*); +SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int); SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); +SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *); +SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *); +SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); +#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL) +SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *); +#endif + /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); @@ -7656,11 +9255,12 @@ typedef struct PCache PCache; ** structure. */ struct PgHdr { - void *pData; /* Content of this page */ + sqlite3_pcache_page *pPage; /* Pcache object page handle */ + void *pData; /* Page data */ void *pExtra; /* Extra content */ PgHdr *pDirty; /* Transient list of dirty pages */ - Pgno pgno; /* Page number for this page */ Pager *pPager; /* The pager this page is part of */ + Pgno pgno; /* Page number for this page */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; /* Hash of page content */ #endif @@ -7774,6 +9374,9 @@ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); #endif +/* Free up as much memory as possible from the page cache */ +SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*); + #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* Try to return memory used by the pcache module to the main memory heap */ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); @@ -7817,7 +9420,7 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); /* ** Figure out if we are dealing with Unix, Windows, or some other ** operating system. After the following block of preprocess macros, -** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER +** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER ** will defined to either 1 or 0. One of the four will be 1. The other ** three will be 0. */ @@ -7827,8 +9430,6 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); # define SQLITE_OS_UNIX 0 # undef SQLITE_OS_WIN # define SQLITE_OS_WIN 0 -# undef SQLITE_OS_OS2 -# define SQLITE_OS_OS2 0 # else # undef SQLITE_OS_OTHER # endif @@ -7839,19 +9440,12 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); # if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) # define SQLITE_OS_WIN 1 # define SQLITE_OS_UNIX 0 -# define SQLITE_OS_OS2 0 -# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__) -# define SQLITE_OS_WIN 0 -# define SQLITE_OS_UNIX 0 -# define SQLITE_OS_OS2 1 # else # define SQLITE_OS_WIN 0 # define SQLITE_OS_UNIX 1 -# define SQLITE_OS_OS2 0 # endif # else # define SQLITE_OS_UNIX 0 -# define SQLITE_OS_OS2 0 # endif #else # ifndef SQLITE_OS_WIN @@ -7859,6 +9453,31 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); # endif #endif +#if SQLITE_OS_WIN +# include +#endif + +/* +** Determine if we are dealing with Windows NT. +** +** We ought to be able to determine if we are compiling for win98 or winNT +** using the _WIN32_WINNT macro as follows: +** +** #if defined(_WIN32_WINNT) +** # define SQLITE_OS_WINNT 1 +** #else +** # define SQLITE_OS_WINNT 0 +** #endif +** +** However, vs2005 does not set _WIN32_WINNT by default, as it ought to, +** so the above test does not work. We'll just assume that everything is +** winNT unless the programmer explicitly says otherwise by setting +** SQLITE_OS_WINNT to 0. +*/ +#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) +# define SQLITE_OS_WINNT 1 +#endif + /* ** Determine if we are dealing with WindowsCE - which has a much ** reduced API. @@ -7869,29 +9488,20 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); # define SQLITE_OS_WINCE 0 #endif +/* +** Determine if we are dealing with WinRT, which provides only a subset of +** the full Win32 API. +*/ +#if !defined(SQLITE_OS_WINRT) +# define SQLITE_OS_WINRT 0 +#endif /* -** Define the maximum size of a temporary filename -*/ -#if SQLITE_OS_WIN -# include -# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) -#elif SQLITE_OS_OS2 -# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY) -# include /* has to be included before os2.h for linking to work */ -# endif -# define INCL_DOSDATETIME -# define INCL_DOSFILEMGR -# define INCL_DOSERRORS -# define INCL_DOSMISC -# define INCL_DOSPROCESS -# define INCL_DOSMODULEMGR -# define INCL_DOSSEMAPHORES -# include -# include -# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP) -#else -# define SQLITE_TEMPNAME_SIZE 200 +** When compiled for WinCE or WinRT, there is no concept of the current +** directory. + */ +#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT +# define SQLITE_CURDIR 1 #endif /* If the SET_FULLSYNC macro is not defined above, then make it @@ -7905,7 +9515,7 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); ** The default size of a disk sector */ #ifndef SQLITE_DEFAULT_SECTOR_SIZE -# define SQLITE_DEFAULT_SECTOR_SIZE 512 +# define SQLITE_DEFAULT_SECTOR_SIZE 4096 #endif /* @@ -8011,7 +9621,11 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); ** 1GB boundary. ** */ -#define PENDING_BYTE sqlite3PendingByte +#ifdef SQLITE_OMIT_WSD +# define PENDING_BYTE (0x40000000) +#else +# define PENDING_BYTE sqlite3PendingByte +#endif #define RESERVED_BYTE (PENDING_BYTE+1) #define SHARED_FIRST (PENDING_BYTE+2) #define SHARED_SIZE 510 @@ -8034,9 +9648,15 @@ SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); +SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **); +SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int); +SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id); +SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int); + /* ** Functions for accessing sqlite3_vfs methods @@ -8053,7 +9673,7 @@ SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); -SQLITE_PRIVATE int sqlite3OsCurrentTime(sqlite3_vfs *, double*); +SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*); /* ** Convenience functions for opening and closing files using @@ -8106,8 +9726,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); ** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. ** ** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. -** -** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2. */ #if !SQLITE_THREADSAFE # define SQLITE_MUTEX_OMIT @@ -8117,8 +9735,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); # define SQLITE_MUTEX_PTHREADS # elif SQLITE_OS_WIN # define SQLITE_MUTEX_W32 -# elif SQLITE_OS_OS2 -# define SQLITE_MUTEX_OS2 # else # define SQLITE_MUTEX_NOOP # endif @@ -8130,14 +9746,17 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); */ #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) #define sqlite3_mutex_free(X) -#define sqlite3_mutex_enter(X) +#define sqlite3_mutex_enter(X) #define sqlite3_mutex_try(X) SQLITE_OK -#define sqlite3_mutex_leave(X) -#define sqlite3_mutex_held(X) 1 -#define sqlite3_mutex_notheld(X) 1 +#define sqlite3_mutex_leave(X) +#define sqlite3_mutex_held(X) ((void)(X),1) +#define sqlite3_mutex_notheld(X) ((void)(X),1) #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() +#define MUTEX_LOGIC(X) +#else +#define MUTEX_LOGIC(X) X #endif /* defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex.h ***********************************************/ @@ -8162,16 +9781,23 @@ struct Db { /* ** An instance of the following structure stores a database schema. ** -** If there are no virtual tables configured in this schema, the -** Schema.db variable is set to NULL. After the first virtual table -** has been added, it is set to point to the database connection -** used to create the connection. Once a virtual table has been -** added to the Schema structure and the Schema.db variable populated, -** only that database connection may use the Schema to prepare -** statements. +** Most Schema objects are associated with a Btree. The exception is +** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** In shared cache mode, a single Schema object can be shared by multiple +** Btrees that refer to the same underlying BtShared object. +** +** Schema objects are automatically deallocated when the last Btree that +** references them is destroyed. The TEMP Schema is manually freed by +** sqlite3_close(). +* +** A thread must be holding a mutex on the corresponding Btree in order +** to access Schema content. This implies that the thread must also be +** holding a mutex on the sqlite3 connection pointer that owns the Btree. +** For a TEMP Schema, only the connection mutex is required. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ + int iGeneration; /* Generation counter. Incremented with each change */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ @@ -8181,14 +9807,11 @@ struct Schema { u8 enc; /* Text encoding used by this database */ u16 flags; /* Flags associated with this schema */ int cache_size; /* Number of pages to use in the cache */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - sqlite3 *db; /* "Owner" connection. See comment above */ -#endif }; /* ** These macros can be used to test, set, or clear bits in the -** Db.flags field. +** Db.pSchema->flags field. */ #define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) #define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) @@ -8196,7 +9819,7 @@ struct Schema { #define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) /* -** Allowed values for the DB.flags field. +** Allowed values for the DB.pSchema->flags field. ** ** The DB_SchemaLoaded flag is set after the database schema has been ** read into internal hash tables. @@ -8241,6 +9864,7 @@ struct Lookaside { u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ int nOut; /* Number of buffers currently checked out */ int mxOut; /* Highwater mark for nOut */ + int anStat[3]; /* 0: hits. 1: size misses. 2: full misses */ LookasideSlot *pFree; /* List of available buffers */ void *pStart; /* First byte of available memory space */ void *pEnd; /* First byte past end of available space */ @@ -8260,65 +9884,45 @@ struct FuncDefHash { }; /* -** Each database is an instance of the following structure. -** -** The sqlite.lastRowid records the last insert rowid generated by an -** insert statement. Inserts on views do not affect its value. Each -** trigger has its own context, so that lastRowid can be updated inside -** triggers as usual. The previous value will be restored once the trigger -** exits. Upon entering a before or instead of trigger, lastRowid is no -** longer (since after version 2.8.12) reset to -1. -** -** The sqlite.nChange does not count changes within triggers and keeps no -** context. It is reset at start of sqlite3_exec. -** The sqlite.lsChange represents the number of changes made by the last -** insert, update, or delete statement. It remains constant throughout the -** length of a statement and is then updated by OP_SetCounts. It keeps a -** context stack just like lastRowid so that the count of changes -** within a trigger is not seen outside the trigger. Changes to views do not -** affect the value of lsChange. -** The sqlite.csChange keeps track of the number of current changes (since -** the last statement) and is used to update sqlite_lsChange. -** -** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 -** store the most recent error code and, if applicable, string. The -** internal function sqlite3Error() is used to set these variables -** consistently. +** Each database connection is an instance of the following structure. */ struct sqlite3 { sqlite3_vfs *pVfs; /* OS Interface */ - int nDb; /* Number of backends currently in use */ + struct Vdbe *pVdbe; /* List of active virtual machines */ + CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ + sqlite3_mutex *mutex; /* Connection mutex */ Db *aDb; /* All backends */ + int nDb; /* Number of backends currently in use */ int flags; /* Miscellaneous flags. See below */ - int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ + i64 lastRowid; /* ROWID of most recent insert (see above) */ + unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ + u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ - u8 dfltJournalMode; /* Default journal mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ + u8 suppressErr; /* Do not issue error messages if true */ + u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ + u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ int nextPagesize; /* Pagesize after VACUUM if >0 */ - int nTable; /* Number of tables in the database */ - CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ - i64 lastRowid; /* ROWID of most recent insert (see above) */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */ - sqlite3_mutex *mutex; /* Connection mutex */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ struct sqlite3InitInfo { /* Information used during initialization */ - int iDb; /* When back is being initialized */ int newTnum; /* Rootpage of table being initialized */ + u8 iDb; /* Which db file is being initialized */ u8 busy; /* TRUE if currently initializing */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ } init; - int nExtension; /* Number of loaded extensions */ - void **aExtension; /* Array of shared library handles */ - struct Vdbe *pVdbe; /* List of active virtual machines */ int activeVdbeCnt; /* Number of VDBEs currently executing */ int writeVdbeCnt; /* Number of active VDBEs that are writing */ + int vdbeExecCnt; /* Number of nested calls to VdbeExec() */ + int nExtension; /* Number of loaded extensions */ + void **aExtension; /* Array of shared library handles */ void (*xTrace)(void*,const char*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ void (*xProfile)(void*,const char*,u64); /* Profiling function */ @@ -8329,6 +9933,10 @@ struct sqlite3 { void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); +#ifndef SQLITE_OMIT_WAL + int (*xWalCallback)(void *, sqlite3 *, const char *, int); + void *pWalArg; +#endif void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; @@ -8351,22 +9959,22 @@ struct sqlite3 { int nProgressOps; /* Number of opcodes for progress callback */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - Hash aModule; /* populated by sqlite3_create_module() */ - Table *pVTab; /* vtab with active Connect/Create method */ - VTable **aVTrans; /* Virtual tables with open transactions */ int nVTrans; /* Allocated size of aVTrans */ + Hash aModule; /* populated by sqlite3_create_module() */ + VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ + VTable **aVTrans; /* Virtual tables with open transactions */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ #endif FuncDefHash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ BusyHandler busyHandler; /* Busy callback */ - int busyTimeout; /* Busy handler timeout, in msec */ Db aDbStatic[2]; /* Static space for the 2 default backends */ Savepoint *pSavepoint; /* List of active savepoints */ + int busyTimeout; /* Busy handler timeout, in msec */ int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ - u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ i64 nDeferredCons; /* Net deferred constraints this transaction. */ + int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MASTER @@ -8395,41 +10003,59 @@ struct sqlite3 { /* ** Possible values for the sqlite3.flags. */ -#define SQLITE_VdbeTrace 0x00000100 /* True to trace VDBE execution */ -#define SQLITE_InternChanges 0x00000200 /* Uncommitted Hash table changes */ -#define SQLITE_FullColNames 0x00000400 /* Show full column names on SELECT */ -#define SQLITE_ShortColNames 0x00000800 /* Show short columns names */ -#define SQLITE_CountRows 0x00001000 /* Count rows changed by INSERT, */ +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000008 /* Show short columns names */ +#define SQLITE_CountRows 0x00000010 /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ -#define SQLITE_NullCallback 0x00002000 /* Invoke the callback once if the */ +#define SQLITE_NullCallback 0x00000020 /* Invoke the callback once if the */ /* result set is empty */ -#define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */ -#define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */ -#define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ -#define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when - ** accessing read-only databases */ -#define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ -#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */ -#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ -#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ -#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ -#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */ -#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ -#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ -#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ +#define SQLITE_SqlTrace 0x00000040 /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing 0x00000080 /* Debug listings of VDBE programs */ +#define SQLITE_WriteSchema 0x00000100 /* OK to update SQLITE_MASTER */ + /* 0x00000200 Unused */ +#define SQLITE_IgnoreChecks 0x00000400 /* Do not enforce check constraints */ +#define SQLITE_ReadUncommitted 0x0000800 /* For shared-cache mode */ +#define SQLITE_LegacyFileFmt 0x00001000 /* Create new databases in format 1 */ +#define SQLITE_FullFSync 0x00002000 /* Use full fsync on the backend */ +#define SQLITE_CkptFullFSync 0x00004000 /* Use full fsync for checkpoint */ +#define SQLITE_RecoveryMode 0x00008000 /* Ignore schema errors */ +#define SQLITE_ReverseOrder 0x00010000 /* Reverse unordered SELECTs */ +#define SQLITE_RecTriggers 0x00020000 /* Enable recursive triggers */ +#define SQLITE_ForeignKeys 0x00040000 /* Enforce foreign key constraints */ +#define SQLITE_AutoIndex 0x00080000 /* Enable automatic indexes */ +#define SQLITE_PreferBuiltin 0x00100000 /* Preference to built-in funcs */ +#define SQLITE_LoadExtension 0x00200000 /* Enable load_extension */ +#define SQLITE_EnableTrigger 0x00400000 /* True to enable triggers */ /* -** Bits of the sqlite3.flags field that are used by the -** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. -** These must be the low-order bits of the flags field. +** Bits of the sqlite3.dbOptFlags field that are used by the +** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to +** selectively disable various optimizations. */ -#define SQLITE_QueryFlattener 0x01 /* Disable query flattening */ -#define SQLITE_ColumnCache 0x02 /* Disable the column cache */ -#define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */ -#define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */ -#define SQLITE_IndexCover 0x10 /* Disable index covering table */ -#define SQLITE_OptMask 0x1f /* Mask of all disablable opts */ +#define SQLITE_QueryFlattener 0x0001 /* Query flattening */ +#define SQLITE_ColumnCache 0x0002 /* Column cache */ +#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ +#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ +#define SQLITE_IdxRealAsInt 0x0010 /* Store REAL as INT in indices */ +#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */ +#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */ +#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */ +#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */ +#define SQLITE_AllOpts 0xffff /* All optimizations */ + +/* +** Macros for testing whether or not optimizations are enabled or disabled. +*/ +#ifndef SQLITE_OMIT_BUILTIN_TEST +#define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0) +#define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0) +#else +#define OptimizationDisabled(db, mask) 0 +#define OptimizationEnabled(db, mask) 1 +#endif /* ** Possible values for the sqlite.magic field. @@ -8441,6 +10067,7 @@ struct sqlite3 { #define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ #define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ #define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ +#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */ /* ** Each SQL function is defined by an instance of the following @@ -8459,18 +10086,42 @@ struct FuncDef { void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pHash; /* Next with a different name but the same hash */ + FuncDestructor *pDestructor; /* Reference counted destructor function */ }; /* -** Possible values for FuncDef.flags +** This structure encapsulates a user-function destructor callback (as +** configured using create_function_v2()) and a reference counter. When +** create_function_v2() is called to create a function with a destructor, +** a single object of this type is allocated. FuncDestructor.nRef is set to +** the number of FuncDef objects created (either 1 or 3, depending on whether +** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor +** member of each of the new FuncDef objects is set to point to the allocated +** FuncDestructor. +** +** Thereafter, when one of the FuncDef objects is deleted, the reference +** count on this object is decremented. When it reaches 0, the destructor +** is invoked and the FuncDestructor structure freed. +*/ +struct FuncDestructor { + int nRef; + void (*xDestroy)(void *); + void *pUserData; +}; + +/* +** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF +** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. There +** are assert() statements in the code to verify this. */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ #define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ -#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ -#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ -#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */ +#define SQLITE_FUNC_COUNT 0x10 /* Built-in count(*) aggregate */ +#define SQLITE_FUNC_COALESCE 0x20 /* Built-in coalesce() or ifnull() function */ +#define SQLITE_FUNC_LENGTH 0x40 /* Built-in length() function */ +#define SQLITE_FUNC_TYPEOF 0x80 /* Built-in typeof() function */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -8498,16 +10149,19 @@ struct FuncDef { ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ - {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ - SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0} + {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL), \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} +#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \ + {nArg, SQLITE_UTF8, (bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags, \ + SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0} #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ - pArg, 0, xFunc, 0, 0, #zName, 0} + pArg, 0, xFunc, 0, 0, #zName, 0, 0} #define LIKEFUNC(zName, nArg, arg, flags) \ - {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0} + {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0} #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ - SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0} + SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0} /* ** All current savepoints are stored in a linked list starting at @@ -8552,52 +10206,33 @@ struct Column { char *zDflt; /* Original text of the default value */ char *zType; /* Data type for this column */ char *zColl; /* Collating sequence. If NULL, use the default */ - u8 notNull; /* True if there is a NOT NULL constraint */ - u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */ + u8 notNull; /* An OE_ code for handling a NOT NULL constraint */ char affinity; /* One of the SQLITE_AFF_... values */ -#ifndef SQLITE_OMIT_VIRTUALTABLE - u8 isHidden; /* True if this column is 'hidden' */ -#endif + u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */ }; +/* Allowed values for Column.colFlags: +*/ +#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */ +#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */ + /* ** A "Collating Sequence" is defined by an instance of the following ** structure. Conceptually, a collating sequence consists of a name and ** a comparison routine that defines the order of that sequence. ** -** There may two separate implementations of the collation function, one -** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that -** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine -** native byte order. When a collation sequence is invoked, SQLite selects -** the version that will require the least expensive encoding -** translations, if any. -** -** The CollSeq.pUser member variable is an extra parameter that passed in -** as the first argument to the UTF-8 comparison function, xCmp. -** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function, -** xCmp16. -** -** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the +** If CollSeq.xCmp is NULL, it means that the ** collating sequence is undefined. Indices built on an undefined ** collating sequence may not be read or written. */ struct CollSeq { char *zName; /* Name of the collating sequence, UTF-8 encoded */ u8 enc; /* Text encoding handled by xCmp() */ - u8 type; /* One of the SQLITE_COLL_... values below */ void *pUser; /* First argument to xCmp() */ int (*xCmp)(void*,int, const void*, int, const void*); void (*xDel)(void*); /* Destructor for pUser */ }; -/* -** Allowed values of CollSeq.type: -*/ -#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */ -#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */ -#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */ -#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */ - /* ** A sort order can be either ASC or DESC. */ @@ -8653,7 +10288,7 @@ struct CollSeq { ** schema is shared, as the implementation often stores the database ** connection handle passed to it via the xConnect() or xCreate() method ** during initialization internally. This database connection handle may -** then used by the virtual table implementation to access real tables +** then be used by the virtual table implementation to access real tables ** within the database. So that they appear as part of the callers ** transaction, these accesses need to be made via the same database ** connection as that used to execute SQL operations on the virtual table. @@ -8687,6 +10322,8 @@ struct VTable { Module *pMod; /* Pointer to module implementation */ sqlite3_vtab *pVtab; /* Pointer to vtab instance */ int nRef; /* Number of pointers to this structure */ + u8 bConstraint; /* True if constraints are supported */ + int iSavepoint; /* Depth of the SAVEPOINT stack */ VTable *pNext; /* Next in linked list (see above) */ }; @@ -8721,29 +10358,29 @@ struct VTable { ** of a SELECT statement. */ struct Table { - sqlite3 *dbMem; /* DB connection used for lookaside allocations. */ char *zName; /* Name of the table or view */ - int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ - int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ - int tnum; /* Root BTree node for this table (see note above) */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ - u16 nRef; /* Number of pointers to this Table */ - u8 tabFlags; /* Mask of TF_* values */ - u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK - Expr *pCheck; /* The AND of all CHECK constraints */ + ExprList *pCheck; /* All CHECK constraints */ #endif + tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */ + int tnum; /* Root BTree node for this table (see note above) */ + i16 iPKey; /* If not negative, use aCol[iPKey] as the primary key */ + i16 nCol; /* Number of columns in this table */ + u16 nRef; /* Number of pointers to this Table */ + u8 tabFlags; /* Mask of TF_* values */ + u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - VTable *pVTable; /* List of VTable objects. */ int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* Text of all module args. [0] is module name */ + VTable *pVTable; /* List of VTable objects. */ #endif Trigger *pTrigger; /* List of triggers stored in pSchema */ Schema *pSchema; /* Schema that contains this table */ @@ -8758,8 +10395,6 @@ struct Table { #define TF_HasPrimaryKey 0x04 /* Table has a primary key */ #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Virtual 0x10 /* Is a virtual table */ -#define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */ - /* @@ -8769,7 +10404,7 @@ struct Table { */ #ifndef SQLITE_OMIT_VIRTUALTABLE # define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0) -# define IsHiddenColumn(X) ((X)->isHidden) +# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0) #else # define IsVirtual(X) 0 # define IsHiddenColumn(X) 0 @@ -8858,9 +10493,9 @@ struct FKey { */ struct KeyInfo { sqlite3 *db; /* The database connection */ - u8 enc; /* Text encoding - one of the TEXT_Utf* values */ + u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ u16 nField; /* Number of entries in aColl[] */ - u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */ + u8 *aSortOrder; /* Sort order for each column. May be NULL */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; @@ -8881,7 +10516,7 @@ struct KeyInfo { struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ u16 nField; /* Number of entries in apMem[] */ - u16 flags; /* Boolean settings. UNPACKED_... below */ + u8 flags; /* Boolean settings. UNPACKED_... below */ i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ Mem *aMem; /* Values */ }; @@ -8889,12 +10524,9 @@ struct UnpackedRecord { /* ** Allowed values of UnpackedRecord.flags */ -#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */ -#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */ -#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */ -#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */ -#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */ -#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */ +#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ +#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ +#define UNPACKED_PREFIX_SEARCH 0x04 /* Ignore final (rowid) field */ /* ** Each SQL index is represented in memory by an @@ -8924,32 +10556,42 @@ struct UnpackedRecord { */ struct Index { char *zName; /* Name of this index */ - int nColumn; /* Number of columns in the table used by this index */ int *aiColumn; /* Which columns are used by this index. 1st is 0 */ - unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ + tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ - int tnum; /* Page containing root of this index in database file */ - u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ - IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */ + int nColumn; /* Number of columns in the table used by this index */ + int tnum; /* Page containing root of this index in database file */ + u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ + u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 bUnordered; /* Use this index for == or IN queries only */ +#ifdef SQLITE_ENABLE_STAT3 + int nSample; /* Number of elements in aSample[] */ + tRowcnt avgEq; /* Average nEq value for key values not in aSample */ + IndexSample *aSample; /* Samples of the left-most key */ +#endif }; /* -** Each sample stored in the sqlite_stat2 table is represented in memory -** using a structure of this type. +** Each sample stored in the sqlite_stat3 table is represented in memory +** using a structure of this type. See documentation at the top of the +** analyze.c source file for additional information. */ struct IndexSample { union { char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ - double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */ + double r; /* Value if eType is SQLITE_FLOAT */ + i64 i; /* Value if eType is SQLITE_INTEGER */ } u; u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ - u8 nByte; /* Size in byte of text or blob. */ + int nByte; /* Size in byte of text or blob. */ + tRowcnt nEq; /* Est. number of rows where the key equals this sample */ + tRowcnt nLt; /* Est. number of rows where key is less than this sample */ + tRowcnt nDLt; /* Est. number of distinct keys less than this sample */ }; /* @@ -8984,8 +10626,9 @@ struct AggInfo { u8 useSortingIdx; /* In direct mode, reference the sorting index rather ** than the source table */ int sortingIdx; /* Cursor number of the sorting index */ - ExprList *pGroupBy; /* The group by clause */ + int sortingIdxPTab; /* Cursor number of pseudo-table */ int nSortingColumn; /* Number of columns in the sorting index */ + ExprList *pGroupBy; /* The group by clause */ struct AggInfo_col { /* For each column used in source tables */ Table *pTab; /* Source table */ int iTable; /* Cursor number of the source table */ @@ -8995,7 +10638,6 @@ struct AggInfo { Expr *pExpr; /* The original expression */ } *aCol; int nColumn; /* Number of used entries in aCol[] */ - int nColumnAlloc; /* Number of slots allocated for aCol[] */ int nAccumulator; /* Number of columns that show through to the output. ** Additional columns are used only as parameters to ** aggregate functions */ @@ -9006,7 +10648,6 @@ struct AggInfo { int iDistinct; /* Ephemeral table used to enforce DISTINCT */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ - int nFuncAlloc; /* Number of slots allocated for aFunc[] */ }; /* @@ -9094,7 +10735,7 @@ struct Expr { u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ - int iValue; /* Integer value if EP_IntValue */ + int iValue; /* Non-negative integer value if EP_IntValue */ } u; /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no @@ -9108,13 +10749,15 @@ struct Expr { ExprList *pList; /* Function arguments or in " IN ( IN (", 8, [ HTMLButtonElement, HTMLFieldSetElement, HTMLInputElement, HTMLSelectElement, HTMLObjectElement, HTMLOutputElement, HTMLSelectElement, HTMLTextAreaElement ] ], ]; -for each(var data in testData) { +for (var data of testData) { fieldset.innerHTML = data[0]; is(fieldset.elements.length, data[1], "fieldset.elements should contain " + data[1] + " elements"); diff --git a/content/html/content/test/test_bug596350.html b/content/html/content/test/test_bug596350.html index 68927b93a9f4..15ea5dde836f 100644 --- a/content/html/content/test/test_bug596350.html +++ b/content/html/content/test/test_bug596350.html @@ -44,7 +44,7 @@ var objects = document.getElementsByTagName("object"); function runTests() { - for each(var data in testData) { + for (var data of testData) { var obj = objects[data[0]]; if (data[1]) { diff --git a/content/html/content/test/test_bug598643.html b/content/html/content/test/test_bug598643.html index a5194adfaf81..dd1e0814b933 100644 --- a/content/html/content/test/test_bug598643.html +++ b/content/html/content/test/test_bug598643.html @@ -60,7 +60,7 @@ input.maxLength = 1; input.value = "foo"; // Too long types. -for each (type in types[0]) { +for (type of types[0]) { input.type = type if (type == 'email') { input.value = "foo@bar.com"; @@ -78,7 +78,7 @@ for each (type in types[0]) { } // Not too long types. -for each (type in types[1]) { +for (type of types[1]) { input.type = type ok(input.validity.valid, "the element should be valid [type=" + type + "]"); ok(!input.validity.tooLong, @@ -86,7 +86,7 @@ for each (type in types[1]) { } // Not too long types but TODO. -for each (type in types[2]) { +for (type of types[2]) { input.type = type ok(input.validity.valid, "the element should be valid [type=" + type + "]"); ok(!input.validity.tooLong, diff --git a/content/html/content/test/test_bug600155.html b/content/html/content/test/test_bug600155.html index f345f6beb5ac..bb10ddfb7707 100644 --- a/content/html/content/test/test_bug600155.html +++ b/content/html/content/test/test_bug600155.html @@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=600155 var subjectForConstraintValidation = [ "input", "select", "textarea" ]; var content = document.getElementById('content'); -for each (var eName in subjectForConstraintValidation) { +for (var eName of subjectForConstraintValidation) { var e = document.createElement(eName); content.appendChild(e); e.setAttribute("x-moz-errormessage", "foo"); diff --git a/content/html/content/test/test_bug607145.html b/content/html/content/test/test_bug607145.html index 7d6e06dac385..6e58a8b16753 100644 --- a/content/html/content/test/test_bug607145.html +++ b/content/html/content/test/test_bug607145.html @@ -52,14 +52,14 @@ function reflectURL(aElement, aAttr) [ "http://a b/", "http://a b/" ], // TODO: doesn't follow the specs, should be "". ]; - for each (var value in values) { + for (var value of values) { aElement[idl] = value[0]; is(aElement[idl], value[1], "." + idl + " value should be " + value[1]); is(aElement.getAttribute(attr), value[0], "@" + attr + " value should be " + value[0]); } - for each (var value in values) { + for (var value of values) { aElement.setAttribute(attr, value[0]); is(aElement[idl], value[1], "." + idl + " value should be " + value[1]); is(aElement.getAttribute(attr), value[0], diff --git a/content/html/content/test/test_bug618948.html b/content/html/content/test/test_bug618948.html index c704beccf329..258041bdb50d 100644 --- a/content/html/content/test/test_bug618948.html +++ b/content/html/content/test/test_bug618948.html @@ -35,7 +35,7 @@ function eventHandler(event) function beginTest() { - for each (var e in events) { + for (var e of events) { handled[e] = false; } @@ -44,7 +44,7 @@ function beginTest() function endTest() { - for each (var e in events) { + for (var e of events) { ok(handled[e], "on" + e + " should have been called"); } diff --git a/content/html/content/test/test_bug659596.html b/content/html/content/test/test_bug659596.html index 50ca18f6746b..a8828cf0a93b 100644 --- a/content/html/content/test/test_bug659596.html +++ b/content/html/content/test/test_bug659596.html @@ -23,7 +23,7 @@ function checkReflection(option, attribute) { // When attribute isn't present. var tests = [ "", "foo" ]; - for each (var test in tests) { + for (var test of tests) { option.removeAttribute(attribute); option.textContent = test; is(option.getAttribute(attribute), null, @@ -39,7 +39,7 @@ function checkReflection(option, attribute) { [ "foo", "bar" ], [ "foo", "" ], ]; - for each (var test in tests) { + for (var test of tests) { option.setAttribute(attribute, test[0]); option.textContent = test[1]; is(option[attribute], option.getAttribute(attribute), @@ -55,7 +55,7 @@ function checkReflection(option, attribute) { [ "", "new" ], [ "foo", "new" ], ]; - for each (var test in tests) { + for (var test of tests) { option.removeAttribute(attribute); option.textContent = test[0]; option[attribute] = test[1] @@ -73,7 +73,7 @@ function checkReflection(option, attribute) { [ "foo", "bar", "new" ], [ "foo", "", "new" ], ]; - for each (var test in tests) { + for (var test of tests) { option.setAttribute(attribute, test[0]); option.textContent = test[1]; option[attribute] = test[2]; diff --git a/content/html/content/test/test_restore_from_parser_fragment.html b/content/html/content/test/test_restore_from_parser_fragment.html index ee5067ebfc00..119ee2102de3 100644 --- a/content/html/content/test/test_restore_from_parser_fragment.html +++ b/content/html/content/test/test_restore_from_parser_fragment.html @@ -39,7 +39,7 @@ var tests = [ var element = null; -for each (var test in tests) { +for (var test of tests) { appendHTML(content, test[1]); element = content.getElementsByTagName(test[0])[0]; is(element.disabled, false, "element shouldn't be disabled"); diff --git a/content/svg/content/test/test_SVGxxxList.xhtml b/content/svg/content/test/test_SVGxxxList.xhtml index 3c939babf3cc..60fa16da0405 100644 --- a/content/svg/content/test/test_SVGxxxList.xhtml +++ b/content/svg/content/test/test_SVGxxxList.xhtml @@ -455,7 +455,7 @@ function run_baseVal_API_tests() var res, threw, items; var eventChecker = new MutationEventChecker; - for each (var t in tests) { + for (var t of tests) { // Test .clear(): @@ -850,7 +850,7 @@ function run_animVal_API_tests() { var threw, item; - for each (var t in tests) { + for (var t of tests) { if (!t.animVal) continue; // SVGStringList isn't animatable @@ -949,7 +949,7 @@ function run_animVal_API_tests() */ function run_basic_setAttribute_tests() { - for each (var t in tests) { + for (var t of tests) { // Since the t.prop, t.baseVal and t.animVal objects should never ever // change, we leave testing of them to our caller so that it can check @@ -1046,7 +1046,7 @@ function run_basic_setAttribute_tests() */ function run_list_mutation_tests() { - for each (var t in tests) { + for (var t of tests) { if (t.animVal) { // Test removeItem() // ================= @@ -1142,7 +1142,7 @@ function run_animation_timeline_tests() { var svg = document.getElementById('svg'); - for each (var t in tests) { + for (var t of tests) { // Skip if there is no animVal for this test or if it is a transform list // since these are handled specially if (!t.animVal || is_transform_attr(t.attr_name)) @@ -1368,7 +1368,7 @@ function run_tests() { // Initialize each test object with some useful properties, and create their // 'animate' elements. Note that 'prop' and 'animVal' may be null. - for each (var t in tests) { + for (var t of tests) { t.element = document.getElementById(t.target_element_id); t.prop = t.prop_name ? t.element[t.prop_name] : null; t.baseVal = ( t.prop || t.element )[t.bv_name]; @@ -1409,7 +1409,7 @@ function run_tests() // After all the other test manipulations, we check that the following // objects have still not changed, since they never should: - for each (var t in tests) { + for (var t of tests) { if (t.prop) { ok(t.prop === t.element[t.prop_name], 'The same '+t.prop_type+' object should ALWAYS be returned for '+ diff --git a/content/svg/content/test/test_isSupported.xhtml b/content/svg/content/test/test_isSupported.xhtml index 7ebdfc6b4457..31d0e280a2be 100644 --- a/content/svg/content/test/test_isSupported.xhtml +++ b/content/svg/content/test/test_isSupported.xhtml @@ -136,7 +136,7 @@ var features = [ ]; function testIsSupported(elem) { - for each (var [feature, version, accepted_result] in features) { + for (var [feature, version, accepted_result] of features) { if (accepted_result) { ok(elem.isSupported(feature, version), "isSupported(" + feature + ", " + version + ") returned wrong value, Was it changed to unsupported feature?"); } else { diff --git a/content/xslt/tests/mochitest/test_bug427060.html b/content/xslt/tests/mochitest/test_bug427060.html index 0cf66c2f1bfc..1bc065d08983 100644 --- a/content/xslt/tests/mochitest/test_bug427060.html +++ b/content/xslt/tests/mochitest/test_bug427060.html @@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=427060 /** Test for Bug 427060 **/ var xmldoc, xsltdoc; -[ xmldoc, xsltdoc ] = [ new DOMParser().parseFromString(xml, "text/xml") for each (xml in [ +[ xmldoc, xsltdoc ] = [ new DOMParser().parseFromString(xml, "text/xml") for (xml of [ '' , diff --git a/content/xslt/tests/mochitest/test_bug440974.html b/content/xslt/tests/mochitest/test_bug440974.html index ad6b30788f0f..c89b7fa79e2a 100644 --- a/content/xslt/tests/mochitest/test_bug440974.html +++ b/content/xslt/tests/mochitest/test_bug440974.html @@ -26,7 +26,7 @@ function isTxResult(node) } var xmldoc, xsltdoc; -[ xmldoc, xsltdoc ] = [ new DOMParser().parseFromString(xml, "text/xml") for each (xml in [ +[ xmldoc, xsltdoc ] = [ new DOMParser().parseFromString(xml, "text/xml") for (xml of [ '123' , diff --git a/content/xslt/tests/mochitest/test_exslt_regex.html b/content/xslt/tests/mochitest/test_exslt_regex.html index 17c2138eae9e..f4e00f9d36a7 100644 --- a/content/xslt/tests/mochitest/test_exslt_regex.html +++ b/content/xslt/tests/mochitest/test_exslt_regex.html @@ -28,7 +28,7 @@ http://www.exslt.org/regexp/index.html expResult: "XSLT is great" } ]; - for each(test in tests) { + for (test of tests) { var style = ' var imports = [ "SimpleTest", "is", "isnot", "ok" ]; -for each (var name in imports) { +for (var name of imports) { window[name] = window.opener.wrappedJSObject[name]; } diff --git a/layout/style/test/test_bug437915.html b/layout/style/test/test_bug437915.html index fbe1e2546466..595c0a643a06 100644 --- a/layout/style/test/test_bug437915.html +++ b/layout/style/test/test_bug437915.html @@ -53,7 +53,7 @@ for (var char in chars) { var mapent = wsmap[is_whitespace]; div.setAttribute("class", "classvalue" + String.fromCharCode(char) + "b") div.setAttribute("title", "a" + String.fromCharCode(char) + "titlevalue") - for each (var prop in ["text-decoration", "visibility"]) { + for (var prop of ["text-decoration", "visibility"]) { is(cs.getPropertyValue(prop), mapent[prop], "Character " + char + " should" + mapent.str + " be treated as whitespace (" diff --git a/layout/style/test/test_bug74880.html b/layout/style/test/test_bug74880.html index d2df215442af..e8a3b30c987d 100644 --- a/layout/style/test/test_bug74880.html +++ b/layout/style/test/test_bug74880.html @@ -54,9 +54,9 @@ var e = document.getElementById("display"); var s = e.style; var c = window.getComputedStyle(e, ""); -for each (var set in gProps) { +for (var set of gProps) { var values = values_for(set); - for each (var val in values) { + for (var val of values) { function check(dir, plogical, pvisual) { var v0 = c.getPropertyValue(pvisual); s.setProperty("direction", dir, ""); diff --git a/layout/style/test/test_selectors.html b/layout/style/test/test_selectors.html index 945e7c267000..1d1dfa9b8337 100644 --- a/layout/style/test/test_selectors.html +++ b/layout/style/test/test_selectors.html @@ -536,7 +536,7 @@ function run() { function idset(ids) { // takes an array of ids return function idset_filter(doc) { var result = []; - for each (var id in ids) + for (var id of ids) result.push(doc.getElementById(id)); return result; } diff --git a/layout/style/test/test_transitions.html b/layout/style/test/test_transitions.html index ba852df7d7dd..75b90a2fccc4 100644 --- a/layout/style/test/test_transitions.html +++ b/layout/style/test/test_transitions.html @@ -151,7 +151,7 @@ for (var tf in timingFunctions) { // Check that the timing function continues even when we restyle in the // middle. var interrupt_tests = []; -for each (var restyleParent in [true, false]) { +for (var restyleParent of [true, false]) { for (var itime = 2; itime < 8; itime += 2) { var p = document.createElement("p"); var t = document.createTextNode("interrupt on " + diff --git a/toolkit/components/passwordmgr/test/pwmgr_common.js b/toolkit/components/passwordmgr/test/pwmgr_common.js index cda8d1a8f16b..d82ef8f227e3 100644 --- a/toolkit/components/passwordmgr/test/pwmgr_common.js +++ b/toolkit/components/passwordmgr/test/pwmgr_common.js @@ -146,7 +146,7 @@ function commonInit() { var disabledHosts = pwmgr.getAllDisabledHosts(); if (disabledHosts.length) { //todo(false, "Warning: wasn't expecting disabled hosts to be present."); - for each (var host in disabledHosts) + for (var host of disabledHosts) pwmgr.setLoginSavingEnabled(host, true); } diff --git a/toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html b/toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html index 912f11f3add1..7ad269dde16e 100644 --- a/toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html +++ b/toolkit/components/passwordmgr/test/test_basic_form_observer_foundLogins.html @@ -103,7 +103,7 @@ var TestObserver = { }; // Initialize the object that stores the results of notifications. -for each (var formID in ["form1", "form2", "form3", "form4", "form5"]) +for (var formID of ["form1", "form2", "form3", "form4", "form5"]) TestObserver.results[formID] = { receivedNotification: false, data: null }; // Add the observer diff --git a/toolkit/components/satchel/test/test_form_autocomplete.html b/toolkit/components/satchel/test/test_form_autocomplete.html index e44f8f179665..88f601b8e0be 100644 --- a/toolkit/components/satchel/test/test_form_autocomplete.html +++ b/toolkit/components/satchel/test/test_form_autocomplete.html @@ -140,7 +140,7 @@ fh.addEntry("searchbar-history", "blacklist test"); var todoTypes = [ "datetime", "date", "month", "week", "time", "datetime-local", "range", "color" ]; var todoInput = document.createElement("input"); -for each (var type in todoTypes) { +for (var type of todoTypes) { todoInput.type = type; todo_is(todoInput.type, type, type + " type shouldn't be implemented"); } From 2995d3615f793fb4b456e5394e71761c9996902d Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Fri, 21 Dec 2012 20:48:36 +0900 Subject: [PATCH 083/217] Bug 804834 - Part 2: Disable for-each-in from content by default. r=waldo --- js/src/frontend/Parser.cpp | 4 ++-- js/src/frontend/Parser.h | 10 ++++++++++ js/src/jsversion.h | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c1db6ef8ab23..d0604aaab8f9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2990,7 +2990,7 @@ Parser::forStatement() pn->setOp(JSOP_ITER); pn->pn_iflags = 0; - if (tokenStream.matchToken(TOK_NAME)) { + if (allowsForEachIn() && tokenStream.matchToken(TOK_NAME)) { if (tokenStream.currentToken().name() == context->names().each) pn->pn_iflags = JSITER_FOREACH; else @@ -5119,7 +5119,7 @@ Parser::comprehensionTail(ParseNode *kid, unsigned blockid, bool isGenexp, pn2->setOp(JSOP_ITER); pn2->pn_iflags = JSITER_ENUMERATE; - if (tokenStream.matchToken(TOK_NAME)) { + if (allowsForEachIn() && tokenStream.matchToken(TOK_NAME)) { if (tokenStream.currentToken().name() == context->names().each) pn2->pn_iflags |= JSITER_FOREACH; else diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index f052304cfdf2..510adfb697fc 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -468,6 +468,16 @@ struct Parser : private AutoGCRooter ParseNode *propertyQualifiedIdentifier(); #endif /* JS_HAS_XML_SUPPORT */ + bool allowsForEachIn() { +#if !JS_HAS_FOR_EACH_IN + return false; +#elif JS_HAS_XML_SUPPORT + return allowsXML() || tokenStream.hasMoarXML(); +#else + return versionNumber() >= JSVERSION_1_6; +#endif + } + bool setAssignmentLhsOps(ParseNode *pn, JSOp op); bool matchInOrOf(bool *isForOfp); }; diff --git a/js/src/jsversion.h b/js/src/jsversion.h index 475525177a63..50e2fa6beb0b 100644 --- a/js/src/jsversion.h +++ b/js/src/jsversion.h @@ -61,6 +61,7 @@ #define JS_HAS_CONST 0 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_FOR_EACH_IN 0 /* has for each (lhs in iterable) */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -82,6 +83,7 @@ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_FOR_EACH_IN 0 /* has for each (lhs in iterable) */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -99,6 +101,7 @@ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_FOR_EACH_IN 1 /* has for each (lhs in iterable) */ #define JS_HAS_GENERATORS 0 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -116,6 +119,7 @@ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_FOR_EACH_IN 1 /* has for each (lhs in iterable) */ #define JS_HAS_GENERATORS 1 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ @@ -133,6 +137,7 @@ #define JS_HAS_CONST 1 /* has JS2 const as alternative var */ #define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ #define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ +#define JS_HAS_FOR_EACH_IN 1 /* has for each (lhs in iterable) */ #define JS_HAS_GENERATORS 1 /* has yield in generator function */ #define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ #define JS_HAS_DESTRUCTURING 2 /* has [a,b] = ... or {p:a,q:b} = ... */ From 17cc116f682423a362159a53bfb51a30beab8b7f Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 21 Dec 2012 15:19:04 +0100 Subject: [PATCH 084/217] Bug 822710 (part 1) - Implement a new getAnnotationsHavingName method. r=Mano sr=gavin --- .../components/places/nsAnnotationService.cpp | 165 +++++++++++++++++- .../components/places/nsAnnotationService.h | 24 +++ .../places/nsIAnnotationService.idl | 57 +++++- .../places/tests/unit/test_annotations.js | 54 ++++++ 4 files changed, 298 insertions(+), 2 deletions(-) diff --git a/toolkit/components/places/nsAnnotationService.cpp b/toolkit/components/places/nsAnnotationService.cpp index e8e7205b678b..edd4cc229c3e 100644 --- a/toolkit/components/places/nsAnnotationService.cpp +++ b/toolkit/components/places/nsAnnotationService.cpp @@ -23,6 +23,7 @@ #include "nsNetCID.h" using namespace mozilla; +using namespace mozilla::places; #define ENSURE_ANNO_TYPE(_type, _statement) \ PR_BEGIN_MACRO \ @@ -47,7 +48,64 @@ const int32_t nsAnnotationService::kAnnoIndex_Type = 7; const int32_t nsAnnotationService::kAnnoIndex_DateAdded = 8; const int32_t nsAnnotationService::kAnnoIndex_LastModified = 9; -using namespace mozilla::places; +namespace mozilla { +namespace places { + +//////////////////////////////////////////////////////////////////////////////// +//// AnnotatedResult + +AnnotatedResult::AnnotatedResult(const nsCString& aGUID, + nsIURI* aURI, + int64_t aItemId, + const nsACString& aAnnotationName, + nsIVariant* aAnnotationValue) +: mGUID(aGUID) +, mURI(aURI) +, mItemId(aItemId) +, mAnnotationName(aAnnotationName) +, mAnnotationValue(aAnnotationValue) +{ +} + +NS_IMETHODIMP +AnnotatedResult::GetGuid(nsACString& _guid) +{ + _guid = mGUID; + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetUri(nsIURI** _uri) +{ + NS_IF_ADDREF(*_uri = mURI); + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetItemId(int64_t* _itemId) +{ + *_itemId = mItemId; + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetAnnotationName(nsACString& _annotationName) +{ + _annotationName = mAnnotationName; + return NS_OK; +} + +NS_IMETHODIMP +AnnotatedResult::GetAnnotationValue(nsIVariant** _annotationValue) +{ + NS_IF_ADDREF(*_annotationValue = mAnnotationValue); + return NS_OK; +} + +NS_IMPL_ISUPPORTS1(AnnotatedResult, mozIAnnotatedResult) + +} // namespace places +} // namespace mozilla PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsAnnotationService, gAnnotationService) @@ -1163,6 +1221,111 @@ nsAnnotationService::GetItemsWithAnnotation(const nsACString& aName, } +NS_IMETHODIMP +nsAnnotationService::GetAnnotationsWithName(const nsACString& aName, + uint32_t* _count, + mozIAnnotatedResult*** _annotations) +{ + NS_ENSURE_ARG(!aName.IsEmpty()); + NS_ENSURE_ARG_POINTER(_annotations); + + *_count = 0; + *_annotations = nullptr; + nsCOMArray annotations; + + nsCOMPtr stmt = mDB->GetStatement( + "SELECT h.guid, h.url, -1, a.type, a.content " + "FROM moz_anno_attributes n " + "JOIN moz_annos a ON n.id = a.anno_attribute_id " + "JOIN moz_places h ON h.id = a.place_id " + "WHERE n.name = :anno_name " + "UNION ALL " + "SELECT b.guid, h.url, b.id, a.type, a.content " + "FROM moz_anno_attributes n " + "JOIN moz_items_annos a ON n.id = a.anno_attribute_id " + "JOIN moz_bookmarks b ON b.id = a.item_id " + "LEFT JOIN moz_places h ON h.id = b.fk " + "WHERE n.name = :anno_name " + ); + NS_ENSURE_STATE(stmt); + mozStorageStatementScoper scoper(stmt); + + nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), + aName); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore = false; + while (NS_SUCCEEDED(rv = stmt->ExecuteStep(&hasMore)) && hasMore) { + nsAutoCString guid; + rv = stmt->GetUTF8String(0, guid); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr uri; + bool uriIsNull = false; + rv = stmt->GetIsNull(1, &uriIsNull); + NS_ENSURE_SUCCESS(rv, rv); + if (!uriIsNull) { + nsAutoCString url; + rv = stmt->GetUTF8String(1, url); + NS_ENSURE_SUCCESS(rv, rv); + rv = NS_NewURI(getter_AddRefs(uri), url); + NS_ENSURE_SUCCESS(rv, rv); + } + + int64_t itemId = stmt->AsInt64(2); + int32_t type = stmt->AsInt32(3); + + nsCOMPtr variant = new nsVariant(); + switch (type) { + case nsIAnnotationService::TYPE_INT32: { + rv = variant->SetAsInt32(stmt->AsInt32(4)); + break; + } + case nsIAnnotationService::TYPE_INT64: { + rv = variant->SetAsInt64(stmt->AsInt64(4)); + break; + } + case nsIAnnotationService::TYPE_DOUBLE: { + rv = variant->SetAsDouble(stmt->AsDouble(4)); + break; + } + case nsIAnnotationService::TYPE_STRING: { + nsAutoString valueString; + rv = stmt->GetString(4, valueString); + NS_ENSURE_SUCCESS(rv, rv); + + rv = variant->SetAsAString(valueString); + break; + } + default: + MOZ_ASSERT("Unsupported annotation type"); + // Move to the next result. + continue; + } + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr anno = new AnnotatedResult(guid, uri, itemId, + aName, variant); + NS_ENSURE_TRUE(annotations.AppendObject(anno), NS_ERROR_OUT_OF_MEMORY); + } + + // Convert to raw array. + if (annotations.Count() == 0) + return NS_OK; + + *_annotations = static_cast + (nsMemory::Alloc(annotations.Count() * sizeof(mozIAnnotatedResult*))); + NS_ENSURE_TRUE(*_annotations, NS_ERROR_OUT_OF_MEMORY); + + *_count = annotations.Count(); + for (uint32_t i = 0; i < *_count; ++i) { + NS_ADDREF((*_annotations)[i] = annotations[i]); + } + + return NS_OK; +} + + nsresult nsAnnotationService::GetItemsWithAnnotationTArray(const nsACString& aName, nsTArray* _results) diff --git a/toolkit/components/places/nsAnnotationService.h b/toolkit/components/places/nsAnnotationService.h index 7927c54e07bf..4f5c8eca0135 100644 --- a/toolkit/components/places/nsAnnotationService.h +++ b/toolkit/components/places/nsAnnotationService.h @@ -17,6 +17,30 @@ #include "nsString.h" #include "mozilla/Attributes.h" +namespace mozilla { +namespace places { + +class AnnotatedResult MOZ_FINAL : public mozIAnnotatedResult +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_MOZIANNOTATEDRESULT + + AnnotatedResult(const nsCString& aGUID, nsIURI* aURI, int64_t aItemd, + const nsACString& aAnnotationName, + nsIVariant* aAnnotationValue); + +private: + const nsCString mGUID; + nsCOMPtr mURI; + const int64_t mItemId; + const nsCString mAnnotationName; + nsCOMPtr mAnnotationValue; +}; + +} // namespace places +} // namespace mozilla + class nsAnnotationService MOZ_FINAL : public nsIAnnotationService , public nsIObserver , public nsSupportsWeakReference diff --git a/toolkit/components/places/nsIAnnotationService.idl b/toolkit/components/places/nsIAnnotationService.idl index 1ee8e4cff8cd..0f018a5e583f 100644 --- a/toolkit/components/places/nsIAnnotationService.idl +++ b/toolkit/components/places/nsIAnnotationService.idl @@ -7,6 +7,7 @@ interface nsIURI; interface nsIVariant; +interface mozIAnnotatedResult; [scriptable, uuid(63fe98e0-6889-4c2c-ac9f-703e4bc25027)] interface nsIAnnotationObserver : nsISupports @@ -31,7 +32,7 @@ interface nsIAnnotationObserver : nsISupports in AUTF8String aName); }; -[scriptable, uuid(ba249b58-346f-42a9-a393-203ae34ec6c4)] +[scriptable, uuid(c7f425cc-a063-49ef-9f4c-b08eb2f1730a)] interface nsIAnnotationService : nsISupports { /** @@ -324,6 +325,19 @@ interface nsIAnnotationService : nsISupports [optional] out unsigned long resultCount, [retval, array, size_is(resultCount)] out long long results); + /** + * Returns a list of mozIAnnotation(s), having a given annotation name. + * + * @param name + * The annotation to search for. + * @return list of mozIAnnotation objects. + * @note binary annotations are not supported and thus skipped. + */ + void getAnnotationsWithName( + in AUTF8String name, + [optional] out unsigned long count, + [retval, array, size_is(count)] out mozIAnnotatedResult results); + /** * Get the names of all annotations for this URI. * @@ -402,3 +416,44 @@ interface nsIAnnotationService : nsISupports nsIURI getAnnotationURI(in nsIURI aURI, in AUTF8String aName); }; + +/** + * Represents a place annotated with a given annotation. If a place has + * multiple annotations, it can be represented by multiple + * mozIAnnotatedResult(s). + */ +[scriptable, uuid(81fd0188-db6a-492e-80b6-f6414913b396)] +interface mozIAnnotatedResult : nsISupports +{ + /** + * The globally unique identifier of the place with this annotation. + * + * @note if itemId is valid this is the guid of the bookmark, otherwise + * of the page. + */ + readonly attribute AUTF8String guid; + + /** + * The URI of the place with this annotation, if available, null otherwise. + */ + readonly attribute nsIURI uri; + + /** + * The bookmark id of the place with this annotation, if available, + * -1 otherwise. + * + * @note if itemId is -1, it doesn't mean the page is not bookmarked, just + * that this annotation is relative to the page, not to the bookmark. + */ + readonly attribute long long itemId; + + /** + * Name of the annotation. + */ + readonly attribute AUTF8String annotationName; + + /** + * Value of the annotation. + */ + readonly attribute nsIVariant annotationValue; +}; diff --git a/toolkit/components/places/tests/unit/test_annotations.js b/toolkit/components/places/tests/unit/test_annotations.js index e5ce4336519d..c6b950442405 100644 --- a/toolkit/components/places/tests/unit/test_annotations.js +++ b/toolkit/components/places/tests/unit/test_annotations.js @@ -357,3 +357,57 @@ add_task(function test_execute() annosvc.removeObserver(annoObserver); }); + +add_test(function test_getAnnotationsHavingName() { + let uri = NetUtil.newURI("http://cat.mozilla.org"); + let id = PlacesUtils.bookmarks.insertBookmark( + PlacesUtils.unfiledBookmarksFolderId, uri, + PlacesUtils.bookmarks.DEFAULT_INDEX, "cat"); + let fid = PlacesUtils.bookmarks.createFolder( + PlacesUtils.unfiledBookmarksFolderId, "pillow", + PlacesUtils.bookmarks.DEFAULT_INDEX); + + const ANNOS = { + "int": 7, + "double": 7.7, + "string": "seven" + }; + for (let name in ANNOS) { + PlacesUtils.annotations.setPageAnnotation( + uri, name, ANNOS[name], 0, + PlacesUtils.annotations.EXPIRE_SESSION); + PlacesUtils.annotations.setItemAnnotation( + id, name, ANNOS[name], 0, + PlacesUtils.annotations.EXPIRE_SESSION); + PlacesUtils.annotations.setItemAnnotation( + fid, name, ANNOS[name], 0, + PlacesUtils.annotations.EXPIRE_SESSION); + } + + for (let name in ANNOS) { + let results = PlacesUtils.annotations.getAnnotationsWithName(name); + do_check_eq(results.length, 3); + + for (let result of results) { + do_check_eq(result.annotationName, name); + do_check_eq(result.annotationValue, ANNOS[name]); + if (result.uri) + do_check_true(result.uri.equals(uri)); + else + do_check_true(result.itemId > 0); + + if (result.itemId != -1) { + if (result.uri) + do_check_eq(result.itemId, id); + else + do_check_eq(result.itemId, fid); + do_check_guid_for_bookmark(result.itemId, result.guid); + } + else { + do_check_guid_for_uri(result.uri, result.guid); + } + } + } + + run_next_test(); +}); From 21b4eb476fbaca9f80ab933a457e9b3f5e63bd3b Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 21 Dec 2012 15:19:06 +0100 Subject: [PATCH 085/217] Bug 822710 (part 2) - Reduce annotations queries on downloads view first load. r=Mano --- .../content/allDownloadsViewOverlay.js | 86 ++++++++++++++++--- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/browser/components/downloads/content/allDownloadsViewOverlay.js b/browser/components/downloads/content/allDownloadsViewOverlay.js index eb65f2ee8414..3f7cd8448bd1 100644 --- a/browser/components/downloads/content/allDownloadsViewOverlay.js +++ b/browser/components/downloads/content/allDownloadsViewOverlay.js @@ -30,6 +30,8 @@ const DOWNLOAD_VIEW_SUPPORTED_COMMANDS = "downloadsCmd_open", "downloadsCmd_show", "downloadsCmd_retry", "downloadsCmd_openReferrer"]; +const NOT_AVAILABLE = Number.MAX_VALUE; + function GetFileForFileURI(aFileURI) Cc["@mozilla.org/network/protocol;1?name=file"] .getService(Ci.nsIFileProtocolHandler) @@ -61,14 +63,18 @@ function GetFileForFileURI(aFileURI) * The data item of a the session download. Required if aPlacesNode is not set * @param [optional] aPlacesNode * The places node for a past download. Required if aDataItem is not set. + * @param [optional] aAnnotations + * Map containing annotations values, to speed up the initial loading. */ -function DownloadElementShell(aDataItem, aPlacesNode) { +function DownloadElementShell(aDataItem, aPlacesNode, aAnnotations) { this._element = document.createElement("richlistitem"); this._element._shell = this; this._element.classList.add("download"); this._element.classList.add("download-state"); + if (aAnnotations) + this._annotations = aAnnotations; if (aDataItem) this.dataItem = aDataItem; if (aPlacesNode) @@ -80,10 +86,11 @@ DownloadElementShell.prototype = { get element() this._element, // The data item for the download + _dataItem: null, get dataItem() this._dataItem, set dataItem(aValue) { - if (this._dataItem = aValue) { + if ((this._dataItem = aValue)) { this._wasDone = this._dataItem.done; this._wasInProgress = this._dataItem.inProgress; } @@ -96,10 +103,15 @@ DownloadElementShell.prototype = { return aValue; }, + _placesNode: null, get placesNode() this._placesNode, set placesNode(aNode) { if (this._placesNode != aNode) { - this._annotations = new Map(); + // Preserve the annotations map if this is the first loading and we got + // cached values. + if (this._placesNode || !this._annotations) { + this._annotations = new Map(); + } this._placesNode = aNode; if (!this._dataItem && this._placesNode) { this._wasInProgress = false; @@ -137,21 +149,30 @@ DownloadElementShell.prototype = { // Helper for getting a places annotation set for the download. _getAnnotation: function DES__getAnnotation(aAnnotation, aDefaultValue) { - if (this._annotations.has(aAnnotation)) - return this._annotations.get(aAnnotation); - let value; - try { - value = PlacesUtils.annotations.getPageAnnotation( - this._downloadURIObj, aAnnotation); + if (this._annotations.has(aAnnotation)) + value = this._annotations.get(aAnnotation); + + // If the value is cached, or we know it doesn't exist, avoid a database + // lookup. + if (value === undefined) { + try { + value = PlacesUtils.annotations.getPageAnnotation( + this._downloadURIObj, aAnnotation); + } + catch(ex) { + value = NOT_AVAILABLE; + } } - catch(ex) { + + if (value === NOT_AVAILABLE) { if (aDefaultValue === undefined) { throw new Error("Could not get required annotation '" + aAnnotation + "' for download with url '" + this.downloadURI + "'"); } value = aDefaultValue; } + this._annotations.set(aAnnotation, value); return value; }, @@ -428,6 +449,7 @@ DownloadElementShell.prototype = { case "downloadsCmd_cancel": return this._dataItem != null; } + return false; }, _getTargetFileOrPartFileIfExists: function DES__getTargetFileOrPartFileIfExists() { @@ -523,6 +545,7 @@ DownloadElementShell.prototype = { case nsIDM.DOWNLOAD_BLOCKED_POLICY: return "downloadsCmd_openReferrer"; } + return ""; } let command = getDefaultCommandForState(this._state); if (this.isCommandEnabled(command)) @@ -584,6 +607,38 @@ DownloadsPlacesView.prototype = { } }, + _getAnnotationsFor: function DPV_getAnnotationsFor(aURI) { + if (!this._cachedAnnotations) { + this._cachedAnnotations = new Map(); + for (let name of [ DESTINATION_FILE_URI_ANNO, + DESTINATION_FILE_NAME_ANNO, + DOWNLOAD_STATE_ANNO ]) { + let results = PlacesUtils.annotations.getAnnotationsWithName(name); + for (let result of results) { + let url = result.uri.spec; + if (!this._cachedAnnotations.has(url)) + this._cachedAnnotations.set(url, new Map()); + let m = this._cachedAnnotations.get(url); + m.set(result.annotationName, result.annotationValue); + } + } + } + + let annotations = this._cachedAnnotations.get(aURI); + if (!annotations) { + // There are no annotations for this entry, that means it is quite old. + // Make up a fake annotations entry with default values. + annotations = new Map(); + annotations.set(DESTINATION_FILE_URI_ANNO, NOT_AVAILABLE); + annotations.set(DESTINATION_FILE_NAME_ANNO, NOT_AVAILABLE); + } + // The state annotation has been added recently, so it's likely missing. + if (!annotations.has(DOWNLOAD_STATE_ANNO)) { + annotations.set(DOWNLOAD_STATE_ANNO, NOT_AVAILABLE); + } + return annotations; + }, + /** * Given a data item for a session download, or a places node for a past * download, updates the view as necessary. @@ -612,7 +667,8 @@ DownloadsPlacesView.prototype = { * to the richlistbox at the end. */ _addDownloadData: - function DPV_addDownload(aDataItem, aPlacesNode, aNewest = false, aDocumentFragment = null) { + function DPV_addDownloadData(aDataItem, aPlacesNode, aNewest = false, + aDocumentFragment = null) { let downloadURI = aPlacesNode ? aPlacesNode.uri : aDataItem.uri; let shellsForURI = this._downloadElementsShellsForURI.get(downloadURI, null); if (!shellsForURI) { @@ -658,9 +714,9 @@ DownloadsPlacesView.prototype = { } if (shouldCreateShell) { - let shell = new DownloadElementShell(aDataItem, aPlacesNode); + let shell = new DownloadElementShell(aDataItem, aPlacesNode, + this._getAnnotationsFor(downloadURI)); newOrUpdatedShell = shell; - element = shell.element; shellsForURI.add(shell); if (aDataItem) this._viewItemsForDataItems.set(aDataItem, shell); @@ -763,8 +819,8 @@ DownloadsPlacesView.prototype = { } }, + _place: "", get place() this._place, - set place(val) { // Don't reload everything if we don't have to. if (this._place == val) { @@ -787,6 +843,7 @@ DownloadsPlacesView.prototype = { return val; }, + _result: null, get result() this._result, set result(val) { if (this._result == val) @@ -1023,6 +1080,7 @@ DownloadsPlacesView.prototype = { // Set the state attribute so that only the appropriate items are displayed. let contextMenu = document.getElementById("downloadsContextMenu"); contextMenu.setAttribute("state", element._shell._state); + return true; }, onKeyPress: function DPV_onKeyPress(aEvent) { From 277a560f9c904e34bd94a3c11dc6da654d753d7b Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 21 Dec 2012 15:19:21 +0100 Subject: [PATCH 086/217] Bug 822343 (Part 3) - Avoid some costs in downloads view appendChild. r=Mano --- .../components/downloads/content/download.css | 2 +- .../components/downloads/content/download.xml | 22 +++++++++---------- .../downloads/allDownloadsViewOverlay.css | 6 ++--- .../downloads/allDownloadsViewOverlay.css | 6 ++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/browser/components/downloads/content/download.css b/browser/components/downloads/content/download.css index bec39ca02ed3..66b608527a2f 100644 --- a/browser/components/downloads/content/download.css +++ b/browser/components/downloads/content/download.css @@ -41,5 +41,5 @@ richlistitem.download button { .download-state:not( [state="1"] /* Finished */) .downloadShow { - visibility: hidden; + display: none; } diff --git a/browser/components/downloads/content/download.xml b/browser/components/downloads/content/download.xml index feb62cbb0c59..d67b0feecebb 100644 --- a/browser/components/downloads/content/download.xml +++ b/browser/components/downloads/content/download.xml @@ -84,17 +84,17 @@ crop="end" xbl:inherits="value=status,tooltiptext=statusTip"/> - - - - - + + + + + diff --git a/browser/themes/gnomestripe/downloads/allDownloadsViewOverlay.css b/browser/themes/gnomestripe/downloads/allDownloadsViewOverlay.css index 43abd71eac50..886d08a85147 100644 --- a/browser/themes/gnomestripe/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/gnomestripe/downloads/allDownloadsViewOverlay.css @@ -173,13 +173,13 @@ richlistitem[type="download"][state="1"]:hover { -moz-image-region: rect(32px, 48px, 48px, 32px); } -richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow { +richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow { -moz-image-region: rect(48px, 16px, 64px, 0px); } -richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover { +richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow:hover { -moz-image-region: rect(48px, 32px, 64px, 16px); } -richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active { +richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow:active { -moz-image-region: rect(48px, 48px, 64px, 32px); } diff --git a/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css b/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css index cac5380ac14d..a64e3a3f9ad4 100644 --- a/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css @@ -178,13 +178,13 @@ richlistitem[type="download"][state="1"]:hover { @media not all and (-moz-windows-default-theme) { %endif -richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow { +richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow { -moz-image-region: rect(48px, 16px, 64px, 0px); } -richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:hover { +richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow:hover { -moz-image-region: rect(48px, 32px, 64px, 16px); } -richlistitem[type="download"][state="1"]:hover > stack > .downloadButton.downloadShow:active { +richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow:active { -moz-image-region: rect(48px, 48px, 64px, 32px); } From c138c1ea53e93ed1633cb600409c8c14765caa09 Mon Sep 17 00:00:00 2001 From: Alfred Kayser Date: Fri, 21 Dec 2012 15:25:50 +0100 Subject: [PATCH 087/217] Bug 815639 - mLocalFile in nsDiskCacheStream doesn't have to be in the class --- netwerk/cache/nsDiskCacheStreams.cpp | 9 +++------ netwerk/cache/nsDiskCacheStreams.h | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/netwerk/cache/nsDiskCacheStreams.cpp b/netwerk/cache/nsDiskCacheStreams.cpp index e8f70668a524..d80d1a0a9108 100644 --- a/netwerk/cache/nsDiskCacheStreams.cpp +++ b/netwerk/cache/nsDiskCacheStreams.cpp @@ -678,18 +678,16 @@ nsDiskCacheStreamIO::OpenCacheFile(int flags, PRFileDesc ** fd) nsresult rv; nsDiskCacheMap * cacheMap = mDevice->CacheMap(); + nsCOMPtr localFile; rv = cacheMap->GetLocalFileForDiskCacheRecord(&mBinding->mRecord, nsDiskCache::kData, !!(flags & PR_CREATE_FILE), - getter_AddRefs(mLocalFile)); + getter_AddRefs(localFile)); if (NS_FAILED(rv)) return rv; // create PRFileDesc for input stream - the 00600 is just for consistency - rv = mLocalFile->OpenNSPRFileDesc(flags, 00600, fd); - if (NS_FAILED(rv)) return rv; // unable to open file - - return NS_OK; + return localFile->OpenNSPRFileDesc(flags, 00600, fd); } @@ -784,7 +782,6 @@ nsDiskCacheStreamIO::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) { size_t usage = aMallocSizeOf(this); - usage += aMallocSizeOf(mLocalFile); usage += aMallocSizeOf(mFD); usage += aMallocSizeOf(mBuffer); diff --git a/netwerk/cache/nsDiskCacheStreams.h b/netwerk/cache/nsDiskCacheStreams.h index 3edf79903f81..1c5f571cc19f 100644 --- a/netwerk/cache/nsDiskCacheStreams.h +++ b/netwerk/cache/nsDiskCacheStreams.h @@ -70,7 +70,6 @@ private: nsDiskCacheDevice * mDevice; nsDiskCacheOutputStream * mOutStream; // not an owning reference int32_t mInStreamCount; - nsCOMPtr mLocalFile; PRFileDesc * mFD; uint32_t mStreamPos; // for Output Streams From 470d172869ae6ca2f3051fc285849e4850aeb1f6 Mon Sep 17 00:00:00 2001 From: EKR Date: Fri, 21 Dec 2012 06:03:22 -0800 Subject: [PATCH 088/217] Bug 820102 - Clean up MediaPipeline threading. r=derf,jesup --- media/mtransport/databuffer.h | 46 +++ media/mtransport/runnable_utils.h | 9 + .../signaling/src/media/VcmSIPCCBinding.cpp | 80 ++++-- .../src/mediapipeline/MediaPipeline.cpp | 265 +++++++++++------- .../src/mediapipeline/MediaPipeline.h | 227 +++++++++------ .../src/peerconnection/PeerConnectionCtx.cpp | 2 + .../src/peerconnection/PeerConnectionImpl.cpp | 29 +- .../src/peerconnection/PeerConnectionImpl.h | 4 +- .../peerconnection/PeerConnectionMedia.cpp | 23 -- .../src/peerconnection/PeerConnectionMedia.h | 28 +- .../signaling/test/mediapipeline_unittest.cpp | 27 +- 11 files changed, 455 insertions(+), 285 deletions(-) create mode 100644 media/mtransport/databuffer.h diff --git a/media/mtransport/databuffer.h b/media/mtransport/databuffer.h new file mode 100644 index 000000000000..f8f890b7f9a9 --- /dev/null +++ b/media/mtransport/databuffer.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +// Original author: ekr@rtfm.com + +#ifndef databuffer_h__ +#define databuffer_h__ +#include +#include +#include + +namespace mozilla { + +class DataBuffer { + public: + DataBuffer() : data_(nullptr), len_(0) {} + DataBuffer(const uint8_t *data, size_t len) { + Assign(data, len); + } + + void Assign(const uint8_t *data, size_t len) { + data_ = new unsigned char[ len ? len : 1]; // Don't depend on new [0]. + memcpy(static_cast(data_.get()), + static_cast(data), len); + len_ = len; + } + + const uint8_t *data() const { return data_; } + size_t len() const { return len_; } + const bool empty() const { return len_ != 0; } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataBuffer) + +private: + ScopedDeleteArray data_; + size_t len_; + + DISALLOW_COPY_ASSIGN(DataBuffer); +}; + +} + +#endif diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h index 09d0715ac953..cfcf213d4720 100644 --- a/media/mtransport/runnable_utils.h +++ b/media/mtransport/runnable_utils.h @@ -40,6 +40,15 @@ class runnable_args_base : public nsRunnable { // Temporary hack. Really we want to have a template which will do this #define RUN_ON_THREAD(t, r, h) ((t && (t != nsRefPtr(do_GetCurrentThread()))) ? t->Dispatch(r, h) : r->Run()) +#define ASSERT_ON_THREAD(t) do { \ + if (t) { \ + bool on; \ + nsresult rv; \ + rv = t->IsOnCurrentThread(&on); \ + MOZ_ASSERT(NS_SUCCEEDED(rv)); \ + MOZ_ASSERT(on); \ + } \ + } while(0) } #endif diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp index defc3d6307bd..d8a17bdfd0c6 100644 --- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp +++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp @@ -909,7 +909,7 @@ static short vcmCreateRemoteStream_m( hints |= nsDOMMediaStream::HINT_CONTENTS_VIDEO; } - sipcc::RemoteSourceStreamInfo* info; + nsRefPtr info; res = pc.impl()->CreateRemoteSourceStreamInfo(hints, &info); if (NS_FAILED(res)) { return VCM_ERROR; @@ -1329,14 +1329,26 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id, if (conduit->ConfigureRecvMediaCodecs(configs)) return VCM_ERROR; + // Now we have all the pieces, create the pipeline - stream->StorePipeline(pc_track_id, + mozilla::RefPtr pipeline = new mozilla::MediaPipelineReceiveAudio( + pc.impl()->GetHandle(), pc.impl()->GetMainThread().get(), pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow)); + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing audio pipeline"); + return VCM_ERROR; + } + + CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", + pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + + stream->StorePipeline(pc_track_id, pipeline); } else if (CC_IS_VIDEO(mcap_id)) { std::vector configs; @@ -1362,13 +1374,24 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id, return VCM_ERROR; // Now we have all the pieces, create the pipeline - stream->StorePipeline(pc_track_id, - new mozilla::MediaPipelineReceiveVideo( - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow)); + mozilla::RefPtr pipeline = + new mozilla::MediaPipelineReceiveVideo( + pc.impl()->GetHandle(), + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing video pipeline"); + return VCM_ERROR; + } + + CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", + pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + + stream->StorePipeline(pc_track_id, pipeline); } else { CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__); return VCM_ERROR; @@ -1936,16 +1959,23 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id, if (!conduit || conduit->ConfigureSendMediaCodec(config)) return VCM_ERROR; - mozilla::RefPtr pipeline = - new mozilla::MediaPipelineTransmit( - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow); + mozilla::RefPtr pipeline = + new mozilla::MediaPipelineTransmit( + pc.impl()->GetHandle(), + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing audio pipeline"); + return VCM_ERROR; + } CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + // Now we have all the pieces, create the pipeline stream->StorePipeline(pc_track_id, pipeline); @@ -1968,18 +1998,24 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id, if (!conduit || conduit->ConfigureSendMediaCodec(config)) return VCM_ERROR; - // Create the pipeline + // Now we have all the pieces, create the pipeline mozilla::RefPtr pipeline = new mozilla::MediaPipelineTransmit( - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow); + pc.impl()->GetHandle(), + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing video pipeline"); + return VCM_ERROR; + } CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); - // Now we have all the pieces, create the pipeline stream->StorePipeline(pc_track_id, pipeline); } else { CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__); diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index b8c90101bf8f..0efe968ac1ee 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -23,6 +23,7 @@ #include "nsError.h" #include "AudioSegment.h" #include "MediaSegment.h" +#include "databuffer.h" #include "transportflow.h" #include "transportlayer.h" #include "transportlayerdtls.h" @@ -32,6 +33,14 @@ using namespace mozilla; +#ifdef DEBUG +// Dial up pipeline logging in debug mode +#define MP_LOG_INFO PR_LOG_WARN +#else +#define MP_LOG_INFO PR_LOG_INFO +#endif + + // Logging context MOZ_MTLOG_MODULE("mediapipeline"); @@ -40,6 +49,7 @@ namespace mozilla { static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp"; nsresult MediaPipeline::Init() { + ASSERT_ON_THREAD(main_thread_); conduit_->AttachTransport(transport_); MOZ_ASSERT(rtp_transport_); @@ -52,7 +62,10 @@ nsresult MediaPipeline::Init() { if (rtp_transport_->state() == TransportLayer::TS_OPEN) { res = TransportReady(rtp_transport_); - NS_ENSURE_SUCCESS(res, res); + if (NS_FAILED(res)) { + MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()"); + return res; + } } else { if (!muxed_) { rtcp_transport_->SignalStateChange.connect(this, @@ -60,7 +73,10 @@ nsresult MediaPipeline::Init() { if (rtcp_transport_->state() == TransportLayer::TS_OPEN) { res = TransportReady(rtcp_transport_); - NS_ENSURE_SUCCESS(res, res); + if (NS_FAILED(res)) { + MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()"); + return res; + } } } } @@ -70,7 +86,9 @@ nsresult MediaPipeline::Init() { // Disconnect us from the transport so that we can cleanly destruct // the pipeline on the main thread. -void MediaPipeline::DetachTransportInt() { +void MediaPipeline::DetachTransport_s() { + ASSERT_ON_THREAD(sts_thread_); + transport_->Detach(); rtp_transport_ = NULL; rtcp_transport_ = NULL; @@ -78,13 +96,13 @@ void MediaPipeline::DetachTransportInt() { void MediaPipeline::DetachTransport() { RUN_ON_THREAD(sts_thread_, - WrapRunnable(this, &MediaPipeline::DetachTransportInt), + WrapRunnable(this, &MediaPipeline::DetachTransport_s), NS_DISPATCH_SYNC); } void MediaPipeline::StateChange(TransportFlow *flow, TransportLayer::State state) { if (state == TransportLayer::TS_OPEN) { - MOZ_MTLOG(PR_LOG_DEBUG, "Flow is ready"); + MOZ_MTLOG(MP_LOG_INFO, "Flow is ready"); TransportReady(flow); } else if (state == TransportLayer::TS_CLOSED || state == TransportLayer::TS_ERROR) { @@ -108,18 +126,21 @@ nsresult MediaPipeline::TransportReady(TransportFlow *flow) { } nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { + MOZ_ASSERT(!description_.empty()); bool rtcp = !(flow == rtp_transport_); State *state = rtcp ? &rtcp_state_ : &rtp_state_; if (*state != MP_CONNECTING) { MOZ_MTLOG(PR_LOG_ERROR, "Transport ready for flow in wrong state:" << - (rtcp ? "rtcp" : "rtp")); + description_ << ": " << (rtcp ? "rtcp" : "rtp")); return NS_ERROR_FAILURE; } nsresult res; - MOZ_MTLOG(PR_LOG_DEBUG, "Transport ready for flow " << (rtcp ? "rtcp" : "rtp")); + MOZ_MTLOG(MP_LOG_INFO, "Transport ready for pipeline " << + static_cast(this) << " flow " << description_ << ": " << + (rtcp ? "rtcp" : "rtp")); // Now instantiate the SRTP objects TransportLayerDtls *dtls = static_cast( @@ -192,14 +213,14 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { rtcp_send_srtp_ = rtp_send_srtp_; rtcp_recv_srtp_ = rtp_recv_srtp_; - MOZ_MTLOG(PR_LOG_DEBUG, "Listening for packets received on " << + MOZ_MTLOG(MP_LOG_INFO, "Listening for packets received on " << static_cast(dtls->downward())); dtls->downward()->SignalPacketReceived.connect(this, &MediaPipeline:: PacketReceived); } else { - MOZ_MTLOG(PR_LOG_DEBUG, "Listening for RTP packets received on " << + MOZ_MTLOG(MP_LOG_INFO, "Listening for RTP packets received on " << static_cast(dtls->downward())); dtls->downward()->SignalPacketReceived.connect(this, @@ -219,7 +240,7 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { return NS_ERROR_FAILURE; } - MOZ_MTLOG(PR_LOG_DEBUG, "Listening for RTCP packets received on " << + MOZ_MTLOG(MP_LOG_INFO, "Listening for RTCP packets received on " << static_cast(dtls->downward())); // Start listening @@ -239,7 +260,7 @@ nsresult MediaPipeline::TransportFailed(TransportFlow *flow) { *state = MP_CLOSED; - MOZ_MTLOG(PR_LOG_DEBUG, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp")); + MOZ_MTLOG(MP_LOG_INFO, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp")); NS_WARNING( "MediaPipeline Transport failed. This is not properly cleaned up yet"); @@ -254,25 +275,10 @@ nsresult MediaPipeline::TransportFailed(TransportFlow *flow) { } -// Wrapper to send a packet on the STS thread. nsresult MediaPipeline::SendPacket(TransportFlow *flow, const void *data, int len) { - nsresult rv; - nsresult res; + ASSERT_ON_THREAD(sts_thread_); - rv = RUN_ON_THREAD(sts_thread_, - WrapRunnableRet(this, &MediaPipeline::SendPacketInt, flow, data, len, &res), - NS_DISPATCH_SYNC); - - // res is invalid unless the dispatch succeeded - if (NS_FAILED(rv)) - return rv; - - return res; -} - -nsresult MediaPipeline::SendPacketInt(TransportFlow *flow, const void *data, - int len) { // Note that we bypass the DTLS layer here TransportLayerDtls *dtls = static_cast( flow->GetLayer(TransportLayerDtls::ID())); @@ -295,37 +301,45 @@ nsresult MediaPipeline::SendPacketInt(TransportFlow *flow, const void *data, void MediaPipeline::increment_rtp_packets_sent() { ++rtp_packets_sent_; - if (!(rtp_packets_sent_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTP packet count " << static_cast(this) + + if (!(rtp_packets_sent_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTP sent packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtp_transport_) << ": " << rtp_packets_sent_); } } void MediaPipeline::increment_rtcp_packets_sent() { ++rtcp_packets_sent_; - if (!(rtcp_packets_sent_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTCP packet count " << static_cast(this) + if (!(rtcp_packets_sent_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTCP sent packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtcp_transport_) << ": " << rtcp_packets_sent_); } } void MediaPipeline::increment_rtp_packets_received() { ++rtp_packets_received_; - if (!(rtp_packets_received_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTP packet count " << static_cast(this) + if (!(rtp_packets_received_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTP received packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtp_transport_) << ": " << rtp_packets_received_); } } void MediaPipeline::increment_rtcp_packets_received() { ++rtcp_packets_received_; - if (!(rtcp_packets_received_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTCP packet count " << static_cast(this) + if (!(rtcp_packets_received_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTCP received packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtcp_transport_) << ": " << rtcp_packets_received_); } } - void MediaPipeline::RtpPacketReceived(TransportLayer *layer, const unsigned char *data, size_t len) { @@ -334,9 +348,10 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer, return; } - // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived - // TODO(ekr@rtfm.com): filter on SSRC for bundle - increment_rtp_packets_received(); + if (!conduit_) { + MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; media disconnected"); + return; + } MOZ_ASSERT(rtp_recv_srtp_); // This should never happen @@ -346,6 +361,10 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer, return; } + // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived + // TODO(ekr@rtfm.com): filter on SSRC for bundle + increment_rtp_packets_received(); + // Make a copy rather than cast away constness ScopedDeletePtr inner_data( new unsigned char[len]); @@ -367,6 +386,17 @@ void MediaPipeline::RtcpPacketReceived(TransportLayer *layer, return; } + if (!conduit_) { + MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; media disconnected"); + return; + } + + if (direction_ == RECEIVE) { + // Discard any RTCP that is being transmitted to us + // This will be unnecessary when we have SSRC filtering. + return; + } + increment_rtcp_packets_received(); MOZ_ASSERT(rtcp_recv_srtp_); // This should never happen @@ -434,25 +464,55 @@ void MediaPipeline::PacketReceived(TransportLayer *layer, } nsresult MediaPipelineTransmit::Init() { + ASSERT_ON_THREAD(main_thread_); + + description_ = pc_ + "| "; + description_ += conduit_->type() == MediaSessionConduit::AUDIO ? + "Transmit audio" : "Transmit video"; + // TODO(ekr@rtfm.com): Check for errors MOZ_MTLOG(PR_LOG_DEBUG, "Attaching pipeline to stream " << static_cast(stream_) << " conduit type=" << (conduit_->type() == MediaSessionConduit::AUDIO ? - "audio" : "video") << - " hints=" << stream_->GetHintContents()); + "audio" : "video")); - // Force this to be a refptr so that we are holding a strong reference - // to the media stream. - nsRefPtr stream (stream_->GetStream()); - return RUN_ON_THREAD(main_thread_, WrapRunnable(stream, - &MediaStream::AddListener, - listener_), - NS_DISPATCH_NORMAL); + stream_->AddListener(listener_); + + return MediaPipeline::Init(); +} + +nsresult MediaPipelineTransmit::TransportReady(TransportFlow *flow) { + // Call base ready function. + MediaPipeline::TransportReady(flow); + + if (flow == rtp_transport_) { + // TODO(ekr@rtfm.com): Move onto MSG thread. + listener_->SetActive(true); + } + + return NS_OK; } nsresult MediaPipeline::PipelineTransport::SendRtpPacket( const void *data, int len) { + nsresult ret; + + nsAutoPtr buf(new DataBuffer(static_cast(data), + len)); + + RUN_ON_THREAD(sts_thread_, + WrapRunnableRet( + RefPtr(this), + &MediaPipeline::PipelineTransport::SendRtpPacket_s, + buf, &ret), + NS_DISPATCH_NORMAL); + + return NS_OK; +} + +nsresult MediaPipeline::PipelineTransport::SendRtpPacket_s( + nsAutoPtr data) { if (!pipeline_) return NS_OK; // Detached @@ -467,14 +527,15 @@ nsresult MediaPipeline::PipelineTransport::SendRtpPacket( // libsrtp enciphers in place, so we need a new, big enough // buffer. // XXX. allocates and deletes one buffer per packet sent. - int max_len = len + SRTP_MAX_EXPANSION; + // Bug 822129 + int max_len = data->len() + SRTP_MAX_EXPANSION; ScopedDeletePtr inner_data( new unsigned char[max_len]); - memcpy(inner_data, data, len); + memcpy(inner_data, data->data(), data->len()); int out_len; nsresult res = pipeline_->rtp_send_srtp_->ProtectRtp(inner_data, - len, + data->len(), max_len, &out_len); if (!NS_SUCCEEDED(res)) @@ -487,6 +548,23 @@ nsresult MediaPipeline::PipelineTransport::SendRtpPacket( nsresult MediaPipeline::PipelineTransport::SendRtcpPacket( const void *data, int len) { + nsresult ret; + + nsAutoPtr buf(new DataBuffer(static_cast(data), + len)); + + RUN_ON_THREAD(sts_thread_, + WrapRunnableRet( + RefPtr(this), + &MediaPipeline::PipelineTransport::SendRtcpPacket_s, + buf, &ret), + NS_DISPATCH_NORMAL); + + return NS_OK; +} + +nsresult MediaPipeline::PipelineTransport::SendRtcpPacket_s( + nsAutoPtr data) { if (!pipeline_) return NS_OK; // Detached @@ -501,14 +579,15 @@ nsresult MediaPipeline::PipelineTransport::SendRtcpPacket( // libsrtp enciphers in place, so we need a new, big enough // buffer. // XXX. allocates and deletes one buffer per packet sent. - int max_len = len + SRTP_MAX_EXPANSION; + // Bug 822129. + int max_len = data->len() + SRTP_MAX_EXPANSION; ScopedDeletePtr inner_data( new unsigned char[max_len]); - memcpy(inner_data, data, len); + memcpy(inner_data, data->data(), data->len()); int out_len; nsresult res = pipeline_->rtcp_send_srtp_->ProtectRtcp(inner_data, - len, + data->len(), max_len, &out_len); if (!NS_SUCCEEDED(res)) @@ -525,22 +604,18 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, TrackTicks offset, uint32_t events, const MediaSegment& queued_media) { - if (!pipeline_) - return; // Detached - MOZ_MTLOG(PR_LOG_DEBUG, "MediaPipeline::NotifyQueuedTrackChanges()"); - // Return early if we are not connected to avoid queueing stuff - // up in the conduit - if (pipeline_->rtp_transport_->state() != TransportLayer::TS_OPEN) { - MOZ_MTLOG(PR_LOG_DEBUG, "Transport not ready yet, dropping packets"); + if (!active_) { + MOZ_MTLOG(PR_LOG_DEBUG, "Discarding packets because transport not ready"); return; } // TODO(ekr@rtfm.com): For now assume that we have only one // track type and it's destined for us + // See bug 784517 if (queued_media.GetType() == MediaSegment::AUDIO) { - if (pipeline_->conduit_->type() != MediaSessionConduit::AUDIO) { + if (conduit_->type() != MediaSessionConduit::AUDIO) { // Ignore data in case we have a muxed stream return; } @@ -549,14 +624,13 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, AudioSegment::ChunkIterator iter(*audio); while(!iter.IsEnded()) { - pipeline_->ProcessAudioChunk(static_cast - (pipeline_->conduit_.get()), - rate, *iter); + ProcessAudioChunk(static_cast(conduit_.get()), + rate, *iter); iter.Next(); } } else if (queued_media.GetType() == MediaSegment::VIDEO) { #ifdef MOZILLA_INTERNAL_API - if (pipeline_->conduit_->type() != MediaSessionConduit::VIDEO) { + if (conduit_->type() != MediaSessionConduit::VIDEO) { // Ignore data in case we have a muxed stream return; } @@ -565,9 +639,8 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, VideoSegment::ChunkIterator iter(*video); while(!iter.IsEnded()) { - pipeline_->ProcessVideoChunk(static_cast - (pipeline_->conduit_.get()), - rate, *iter); + ProcessVideoChunk(static_cast(conduit_.get()), + rate, *iter); iter.Next(); } #endif @@ -576,9 +649,10 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, } } -void MediaPipelineTransmit::ProcessAudioChunk(AudioSessionConduit *conduit, - TrackRate rate, - AudioChunk& chunk) { +void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk( + AudioSessionConduit *conduit, + TrackRate rate, + AudioChunk& chunk) { // TODO(ekr@rtfm.com): Do more than one channel nsAutoArrayPtr samples(new int16_t[chunk.mDuration]); @@ -612,9 +686,10 @@ void MediaPipelineTransmit::ProcessAudioChunk(AudioSessionConduit *conduit, } #ifdef MOZILLA_INTERNAL_API -void MediaPipelineTransmit::ProcessVideoChunk(VideoSessionConduit *conduit, - TrackRate rate, - VideoChunk& chunk) { +void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk( + VideoSessionConduit* conduit, + TrackRate rate, + VideoChunk& chunk) { // We now need to send the video frame to the other side layers::Image *img = chunk.mFrame.GetImage(); if (!img) { @@ -667,27 +742,20 @@ void MediaPipelineTransmit::ProcessVideoChunk(VideoSessionConduit *conduit, #endif nsresult MediaPipelineReceiveAudio::Init() { + ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(PR_LOG_DEBUG, __FUNCTION__); - // Force this to be a refptr so that we are holding a strong reference - // to the media stream. - nsRefPtr stream (stream_->GetStream()); - return RUN_ON_THREAD(main_thread_, WrapRunnable(stream, - &MediaStream::AddListener, - listener_), - NS_DISPATCH_NORMAL); + description_ = pc_ + "| Receive audio"; + + stream_->AddListener(listener_); + + return MediaPipelineReceive::Init(); } void MediaPipelineReceiveAudio::PipelineListener:: NotifyPull(MediaStreamGraph* graph, StreamTime total) { - if (!pipeline_) - return; // Detached - - SourceMediaStream *source = - pipeline_->stream_->GetStream()->AsSourceStream(); - - MOZ_ASSERT(source); - if (!source) { + MOZ_ASSERT(source_); + if (!source_) { MOZ_MTLOG(PR_LOG_ERROR, "NotifyPull() called from a non-SourceMediaStream"); return; } @@ -715,7 +783,7 @@ NotifyPull(MediaStreamGraph* graph, StreamTime total) { int samples_length; MediaConduitErrorCode err = - static_cast(pipeline_->conduit_.get())->GetAudioFrame( + static_cast(conduit_.get())->GetAudioFrame( static_cast(samples->Data()), 16000, // Sampling rate fixed at 16 kHz for now 0, // TODO(ekr@rtfm.com): better estimate of capture delay @@ -731,20 +799,21 @@ NotifyPull(MediaStreamGraph* graph, StreamTime total) { segment.AppendFrames(samples.forget(), samples_length, 0, samples_length, AUDIO_FORMAT_S16); - char buf[32]; - PR_snprintf(buf, 32, "%p", source); - source->AppendToTrack(1, // TODO(ekr@rtfm.com): Track ID - &segment); + source_->AppendToTrack(1, // TODO(ekr@rtfm.com): Track ID + &segment); } } nsresult MediaPipelineReceiveVideo::Init() { + ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(PR_LOG_DEBUG, __FUNCTION__); + description_ = pc_ + "| Receive video"; + static_cast(conduit_.get())-> AttachRenderer(renderer_); - return NS_OK; + return MediaPipelineReceive::Init(); } MediaPipelineReceiveVideo::PipelineRenderer::PipelineRenderer( @@ -755,7 +824,7 @@ MediaPipelineReceiveVideo::PipelineRenderer::PipelineRenderer( #ifdef MOZILLA_INTERNAL_API image_container_ = layers::LayerManager::CreateImageContainer(); SourceMediaStream *source = - pipeline_->stream_->GetStream()->AsSourceStream(); + pipeline_->stream_->AsSourceStream(); source->AddTrack(1 /* Track ID */, 30, 0, new VideoSegment()); source->AdvanceKnownTracksTime(STREAM_TIME_MAX); #endif @@ -768,7 +837,7 @@ void MediaPipelineReceiveVideo::PipelineRenderer::RenderVideoFrame( int64_t render_time) { #ifdef MOZILLA_INTERNAL_API SourceMediaStream *source = - pipeline_->stream_->GetStream()->AsSourceStream(); + pipeline_->stream_->AsSourceStream(); // Create a video frame and append it to the track. ImageFormat format = PLANAR_YCBCR; diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h index c1738b78ae89..787b75f47995 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -17,6 +17,8 @@ #include "MediaConduitInterface.h" #include "AudioSegment.h" #include "SrtpFlow.h" +#include "databuffer.h" +#include "runnable_utils.h" #include "transportflow.h" #ifdef MOZILLA_INTERNAL_API @@ -35,14 +37,35 @@ namespace mozilla { // network -> transport -> [us] -> conduit -> [us] -> stream -> Playout // // The boxes labeled [us] are just bridge logic implemented in this class +// +// We have to deal with a number of threads: +// +// GSM: +// * Assembles the pipeline +// SocketTransportService +// * Receives notification that ICE and DTLS have completed +// * Processes incoming network data and passes it to the conduit +// * Processes outgoing RTP and RTCP +// MediaStreamGraph +// * Receives outgoing data from the MediaStreamGraph +// * Receives pull requests for more data from the +// MediaStreamGraph +// One or another GIPS threads +// * Receives RTCP messages to send to the other side +// * Processes video frames GIPS wants to render +// +// For a transmitting conduit, "output" is RTP and "input" is RTCP. +// For a receiving conduit, "input" is RTP and "output" is RTCP. +// class MediaPipeline : public sigslot::has_slots<> { public: enum Direction { TRANSMIT, RECEIVE }; enum State { MP_CONNECTING, MP_OPEN, MP_CLOSED }; - MediaPipeline(Direction direction, + MediaPipeline(const std::string& pc, + Direction direction, nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) @@ -64,21 +87,31 @@ class MediaPipeline : public sigslot::has_slots<> { rtcp_packets_sent_(0), rtp_packets_received_(0), rtcp_packets_received_(0), - muxed_((rtcp_transport_ == NULL) || (rtp_transport_ == rtcp_transport_)) { - Init(); + muxed_((rtcp_transport_ == NULL) || (rtp_transport_ == rtcp_transport_)), + pc_(pc), + description_() { } virtual ~MediaPipeline() { + MOZ_ASSERT(!stream_); // Check that we have shut down already. + } + + void Shutdown() { + ASSERT_ON_THREAD(main_thread_); + // First shut down networking and then disconnect from + // the media streams. DetachTransport() is sync so + // we are sure that the transport is shut down before + // we touch stream_ or conduit_. DetachTransport(); + if (stream_) { + DetachMediaStream(); + } } virtual nsresult Init(); virtual Direction direction() const { return direction_; } - virtual void DetachMediaStream() {} - virtual void DetachTransport(); - int rtp_packets_sent() const { return rtp_packets_sent_; } int rtcp_packets_sent() const { return rtp_packets_sent_; } int rtp_packets_received() const { return rtp_packets_received_; } @@ -87,13 +120,17 @@ class MediaPipeline : public sigslot::has_slots<> { // Thread counting NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline) - protected: + protected: + virtual void DetachMediaStream() {} + // Separate class to allow ref counting class PipelineTransport : public TransportInterface { public: // Implement the TransportInterface functions PipelineTransport(MediaPipeline *pipeline) - : pipeline_(pipeline) {} + : pipeline_(pipeline), + sts_thread_(pipeline->sts_thread_) {} + void Detach() { pipeline_ = NULL; } MediaPipeline *pipeline() const { return pipeline_; } @@ -101,7 +138,11 @@ class MediaPipeline : public sigslot::has_slots<> { virtual nsresult SendRtcpPacket(const void* data, int len); private: + virtual nsresult SendRtpPacket_s(nsAutoPtr data); + virtual nsresult SendRtcpPacket_s(nsAutoPtr data); + MediaPipeline *pipeline_; // Raw pointer to avoid cycles + nsCOMPtr sts_thread_; }; friend class PipelineTransport; @@ -124,31 +165,51 @@ class MediaPipeline : public sigslot::has_slots<> { void PacketReceived(TransportLayer *layer, const unsigned char *data, size_t len); - Direction direction_; - nsDOMMediaStream* stream_; - RefPtr conduit_; + RefPtr stream_; // A pointer to the stream we are servicing. + // Written on the main thread. + // Used on STS and MediaStreamGraph threads. + RefPtr conduit_; // Our conduit. Written on the main + // thread. Read on STS thread. + + // The transport objects. Read/written on STS thread. RefPtr rtp_transport_; State rtp_state_; RefPtr rtcp_transport_; State rtcp_state_; + + // Pointers to the threads we need. Initialized at creation + // and used all over the place. nsCOMPtr main_thread_; nsCOMPtr sts_thread_; + + // Created on Init. Referenced by the conduit and eventually + // destroyed on the STS thread. RefPtr transport_; - bool transport_connected_; + + // Used only on STS thread. RefPtr rtp_send_srtp_; RefPtr rtcp_send_srtp_; RefPtr rtp_recv_srtp_; RefPtr rtcp_recv_srtp_; + + // Written only on STS thread. May be read on other + // threads but since there is no mutex, the values + // will only be approximate. int rtp_packets_sent_; int rtcp_packets_sent_; int rtp_packets_received_; int rtcp_packets_received_; + + // Written on Init. Read on STS thread. bool muxed_; + std::string pc_; + std::string description_; private: - virtual void DetachTransportInt(); - nsresult SendPacketInt(TransportFlow *flow, const void* data, int len); + void DetachTransport(); + void DetachTransport_s(); + nsresult TransportReadyInt(TransportFlow *flow); bool IsRtp(const unsigned char *data, size_t len); @@ -159,47 +220,44 @@ class MediaPipeline : public sigslot::has_slots<> { // and transmitting to the network. class MediaPipelineTransmit : public MediaPipeline { public: - MediaPipelineTransmit(nsCOMPtr main_thread, + MediaPipelineTransmit(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipeline(TRANSMIT, main_thread, sts_thread, + MediaPipeline(pc, TRANSMIT, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), - listener_(new PipelineListener(this)) { - Init(); // TODO(ekr@rtfm.com): ignoring error - } + listener_(new PipelineListener(conduit)) {} // Initialize (stuff here may fail) - nsresult Init(); - - virtual ~MediaPipelineTransmit() { - if (stream_ && listener_){ - stream_->GetStream()->RemoveListener(listener_); - - // These shouldn't be necessary, but just to make sure - // that if we have messed up ownership somehow the - // interfaces just abort. - listener_->Detach(); - } - } + virtual nsresult Init(); + // Called on the main thread. virtual void DetachMediaStream() { - // TODO(ekr@rtfm.com): Are multiple removes a problem? - stream_->GetStream()->RemoveListener(listener_); - stream_ = NULL; - listener_->Detach(); + ASSERT_ON_THREAD(main_thread_); + stream_->RemoveListener(listener_); + // Remove our reference so that when the MediaStreamGraph + // releases the listener, it will be destroyed. + listener_ = nullptr; + stream_ = nullptr; } + // Override MediaPipeline::TransportReady. + virtual nsresult TransportReady(TransportFlow *flow); + // Separate class to allow ref counting class PipelineListener : public MediaStreamListener { public: - PipelineListener(MediaPipelineTransmit *pipeline) : - pipeline_(pipeline) {} - void Detach() { pipeline_ = NULL; } + PipelineListener(const RefPtr& conduit) + : conduit_(conduit), active_(false) {} + // XXX. This is not thread-safe but the hazard is just + // that active_ = true takes a while to propagate. Revisit + // when 823600 lands. + void SetActive(bool active) { active_ = active; } // Implement MediaStreamListener virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, @@ -210,18 +268,19 @@ class MediaPipelineTransmit : public MediaPipeline { virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {} private: - MediaPipelineTransmit *pipeline_; // Raw pointer to avoid cycles + virtual void ProcessAudioChunk(AudioSessionConduit *conduit, + TrackRate rate, AudioChunk& chunk); +#ifdef MOZILLA_INTERNAL_API + virtual void ProcessVideoChunk(VideoSessionConduit *conduit, + TrackRate rate, VideoChunk& chunk); +#endif + RefPtr conduit_; + volatile bool active_; }; - friend class PipelineListener; private: - virtual void ProcessAudioChunk(AudioSessionConduit *conduit, - TrackRate rate, AudioChunk& chunk); -#ifdef MOZILLA_INTERNAL_API - virtual void ProcessVideoChunk(VideoSessionConduit *conduit, - TrackRate rate, VideoChunk& chunk); -#endif - RefPtr listener_; +RefPtr listener_; + }; @@ -229,13 +288,14 @@ class MediaPipelineTransmit : public MediaPipeline { // rendering video. class MediaPipelineReceive : public MediaPipeline { public: - MediaPipelineReceive(nsCOMPtr main_thread, + MediaPipelineReceive(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipeline(RECEIVE, main_thread, sts_thread, + MediaPipeline(pc, RECEIVE, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), segments_added_(0) { @@ -254,41 +314,40 @@ class MediaPipelineReceive : public MediaPipeline { // rendering audio. class MediaPipelineReceiveAudio : public MediaPipelineReceive { public: - MediaPipelineReceiveAudio(nsCOMPtr main_thread, + MediaPipelineReceiveAudio(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipelineReceive(main_thread, sts_thread, + MediaPipelineReceive(pc, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), - listener_(new PipelineListener(this)) { - Init(); - } - - ~MediaPipelineReceiveAudio() { - if (stream_ && listener_) { - stream_->GetStream()->RemoveListener(listener_); - listener_->Detach(); - } + listener_(new PipelineListener(stream->AsSourceStream(), + conduit)) { } virtual void DetachMediaStream() { - // TODO(ekr@rtfm.com): Are multiple removes a problem? - stream_->GetStream()->RemoveListener(listener_); - stream_ = NULL; - listener_->Detach(); + ASSERT_ON_THREAD(main_thread_); + stream_->RemoveListener(listener_); + // Remove our reference so that when the MediaStreamGraph + // releases the listener, it will be destroyed. + listener_ = nullptr; + stream_ = nullptr; } + virtual nsresult Init(); + private: // Separate class to allow ref counting class PipelineListener : public MediaStreamListener { public: - PipelineListener(MediaPipelineReceiveAudio *pipeline) - : pipeline_(pipeline), - played_(0) {} - void Detach() { pipeline_ = NULL; } + PipelineListener(SourceMediaStream * source, + const RefPtr& conduit) + : source_(source), + conduit_(conduit), + played_(0) {} // Implement MediaStreamListener virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, @@ -299,12 +358,10 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime); private: - MediaPipelineReceiveAudio *pipeline_; // Raw pointer to avoid cycles + SourceMediaStream *source_; + RefPtr conduit_; StreamTime played_; }; - friend class PipelineListener; - - nsresult Init(); RefPtr listener_; }; @@ -314,22 +371,29 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { // rendering video. class MediaPipelineReceiveVideo : public MediaPipelineReceive { public: - MediaPipelineReceiveVideo(nsCOMPtr main_thread, + MediaPipelineReceiveVideo(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipelineReceive(main_thread, sts_thread, + MediaPipelineReceive(pc, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), renderer_(new PipelineRenderer(this)) { - Init(); } - ~MediaPipelineReceiveVideo() { + // Called on the main thread. + virtual void DetachMediaStream() { + ASSERT_ON_THREAD(main_thread_); + conduit_ = nullptr; // Force synchronous destruction so we + // stop generating video. + stream_ = nullptr; } + virtual nsresult Init(); + private: class PipelineRenderer : public VideoRenderer { public: @@ -360,7 +424,6 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive { }; friend class PipelineRenderer; - nsresult Init(); RefPtr renderer_; }; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index 29e13a9089fa..cc7e2b72596a 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -31,7 +31,9 @@ nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread) { gMainThread = mainThread; CSF::VcmSIPCCBinding::setMainThread(gMainThread); } else { +#ifdef MOZILLA_INTERNAL_API MOZ_ASSERT(gMainThread == mainThread); +#endif } nsresult res; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 55417fe2cc95..b9930600ff06 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -260,19 +260,7 @@ PeerConnectionImpl::MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval) } nsresult -PeerConnectionImpl::MakeRemoteSource(nsDOMMediaStream* aStream, RemoteSourceStreamInfo** aInfo) -{ - MOZ_ASSERT(aInfo); - MOZ_ASSERT(aStream); - - // TODO(ekr@rtfm.com): Add the track info with the first segment - nsRefPtr remote = new RemoteSourceStreamInfo(aStream); - NS_ADDREF(*aInfo = remote); - return NS_OK; -} - -nsresult -PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStreamInfo** aInfo) +PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, nsRefPtr* aInfo) { MOZ_ASSERT(aInfo); PC_AUTO_ENTER_API_CALL_NO_CHECK(); @@ -288,19 +276,8 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStr static_cast(comstream->GetStream())->SetPullEnabled(true); nsRefPtr remote; - if (!mThread || NS_IsMainThread()) { - remote = new RemoteSourceStreamInfo(comstream); - NS_ADDREF(*aInfo = remote); - return NS_OK; - } - - mThread->Dispatch(WrapRunnableNMRet( - &PeerConnectionImpl::MakeRemoteSource, comstream, aInfo, &res - ), NS_DISPATCH_SYNC); - - if (NS_FAILED(res)) { - return res; - } + remote = new RemoteSourceStreamInfo(comstream); + *aInfo = remote; return NS_OK; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 57f9f5231201..f393a2c16df6 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -119,14 +119,14 @@ public: static nsresult ConvertConstraints( const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx); static nsresult MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aStream); - static nsresult MakeRemoteSource(nsDOMMediaStream* aStream, RemoteSourceStreamInfo** aInfo); Role GetRole() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mRole; } - nsresult CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStreamInfo** aInfo); + nsresult CreateRemoteSourceStreamInfo(uint32_t aHint, + nsRefPtr* aInfo); // Implementation of the only observer we need virtual void onCallEvent( diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index c7c404e41dbd..774e849858a6 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -28,22 +28,6 @@ static const char* logTag = "PeerConnectionMedia"; static const mozilla::TrackID TRACK_AUDIO = 0; static const mozilla::TrackID TRACK_VIDEO = 1; -/* We get this callback in order to find out which tracks are audio and which - * are video. We should get this callback right away for existing streams after - * we add this class as a listener. - */ -void -LocalSourceStreamInfo::NotifyQueuedTrackChanges( - mozilla::MediaStreamGraph* aGraph, - mozilla::TrackID aID, - mozilla::TrackRate aTrackRate, - mozilla::TrackTicks aTrackOffset, - uint32_t aTrackEvents, - const mozilla::MediaSegment& aQueuedMedia) -{ - /* TODO: use this callback to keep track of changes to the MediaStream */ -} - /* If the ExpectAudio hint is on we will add a track at the default first * audio track ID (0) * FIX - Do we need to iterate over the tracks instead of taking these hints? @@ -197,13 +181,6 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream localSourceStream->ExpectVideo(TRACK_VIDEO); } - // Make it the listener for info from the MediaStream and add it to the list - mozilla::MediaStream *plainMediaStream = stream->GetStream(); - - if (plainMediaStream) { - plainMediaStream->AddListener(localSourceStream); - } - mLocalSourceStreams.AppendElement(localSourceStream); PR_Unlock(mLocalSourceStreamsLock); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index f46cb8eabf2b..38b76580e379 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -157,7 +157,7 @@ class Fake_VideoGenerator { }; #endif -class LocalSourceStreamInfo : public mozilla::MediaStreamListener { +class LocalSourceStreamInfo { public: LocalSourceStreamInfo(nsDOMMediaStream* aMediaStream) : mMediaStream(aMediaStream) {} @@ -165,24 +165,6 @@ public: mMediaStream = NULL; } - /** - * Notify that changes to one of the stream tracks have been queued. - * aTrackEvents can be any combination of TRACK_EVENT_CREATED and - * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track - * at aTrackOffset (relative to the start of the stream). - */ - virtual void NotifyQueuedTrackChanges( - mozilla::MediaStreamGraph* aGraph, - mozilla::TrackID aID, - mozilla::TrackRate aTrackRate, - mozilla::TrackTicks aTrackOffset, - uint32_t aTrackEvents, - const mozilla::MediaSegment& aQueuedMedia - ); - - virtual void NotifyPull(mozilla::MediaStreamGraph* aGraph, - mozilla::StreamTime aDesiredTime) {} - nsDOMMediaStream* GetMediaStream() { return mMediaStream; } @@ -194,18 +176,16 @@ public: unsigned VideoTrackCount(); void Detach() { - // Disconnect my own listener - GetMediaStream()->GetStream()->RemoveListener(this); - // walk through all the MediaPipelines and disconnect them. for (std::map >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { - it->second->DetachMediaStream(); + it->second->Shutdown(); } mMediaStream = NULL; } + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo) private: std::map > mPipelines; nsRefPtr mMediaStream; @@ -229,7 +209,7 @@ class RemoteSourceStreamInfo { for (std::map >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { - it->second->DetachMediaStream(); + it->second->Shutdown(); } mMediaStream = NULL; } diff --git a/media/webrtc/signaling/test/mediapipeline_unittest.cpp b/media/webrtc/signaling/test/mediapipeline_unittest.cpp index 908efe99f082..072962a1a3b5 100644 --- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp +++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp @@ -137,9 +137,15 @@ class TestAgentSend : public TestAgent { ConfigureSendMediaCodec(&audio_config_); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - audio_pipeline_ = new mozilla::MediaPipelineTransmit(NULL, - test_utils->sts_target(), - audio_, audio_conduit_, audio_flow_, NULL); + std::string test_pc("PC"); + + audio_pipeline_ = new mozilla::MediaPipelineTransmit( + test_pc, + NULL, + test_utils->sts_target(), + audio_->GetStream(), audio_conduit_, audio_flow_, NULL); + + audio_pipeline_->Init(); // video_ = new Fake_nsDOMMediaStream(new Fake_VideoStreamSource()); // video_pipeline_ = new mozilla::MediaPipelineTransmit(video_, video_conduit_, &video_flow_, &video_flow_); @@ -170,11 +176,16 @@ class TestAgentReceive : public TestAgent { ConfigureRecvMediaCodecs(codecs); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(NULL, - test_utils->sts_target(), - audio_, - static_cast(audio_conduit_.get()), - audio_flow_, NULL); + std::string test_pc("PC"); + audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio( + test_pc, + NULL, + test_utils->sts_target(), + audio_->GetStream(), + static_cast(audio_conduit_.get()), + audio_flow_, NULL); + + audio_pipeline_->Init(); } private: From ac1209e3ac26f8fb7f5de5d1466e6dc5e5f2126b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez?= Date: Fri, 21 Dec 2012 13:30:52 +0100 Subject: [PATCH 089/217] Bug 823880 - B2G SMS: Add permissions for sms-sent system message; r=daleharvey --- dom/messages/SystemMessagePermissionsChecker.jsm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index a52287ba457d..68f20a742a56 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -77,6 +77,9 @@ this.SystemMessagePermissionsTable = { "sms-received": { "sms": [] }, + "sms-sent": { + "sms": [] + }, "telephony-new-call": { "telephony": [] }, From e6f9ea6e63bf3c34b5ef491cbd2451dd122d8d21 Mon Sep 17 00:00:00 2001 From: EKR Date: Fri, 21 Dec 2012 07:11:02 -0800 Subject: [PATCH 090/217] Bug 821292 - Clean up runnables when RUN_ON_THREAD runs on the same thread. r=jesup --- media/mtransport/runnable_utils.h | 13 +++++- .../test/runnable_utils_unittest.cpp | 46 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h index cfcf213d4720..e45d2ddfe58a 100644 --- a/media/mtransport/runnable_utils.h +++ b/media/mtransport/runnable_utils.h @@ -10,13 +10,13 @@ #define runnable_utils_h__ #include "nsThreadUtils.h" +#include "mozilla/RefPtr.h" // Abstract base class for all of our templates namespace mozilla { class runnable_args_base : public nsRunnable { public: - NS_IMETHOD Run() = 0; }; @@ -39,7 +39,16 @@ class runnable_args_base : public nsRunnable { #include "runnable_utils_generated.h" // Temporary hack. Really we want to have a template which will do this -#define RUN_ON_THREAD(t, r, h) ((t && (t != nsRefPtr(do_GetCurrentThread()))) ? t->Dispatch(r, h) : r->Run()) +static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, nsIRunnable *runnable, uint32_t flags) { + RefPtr runnable_ref(runnable); + + if (thread && (thread != nsRefPtr(do_GetCurrentThread()))) { + return thread->Dispatch(runnable_ref, flags); + } + + return runnable_ref->Run(); +} + #define ASSERT_ON_THREAD(t) do { \ if (t) { \ bool on; \ diff --git a/media/mtransport/test/runnable_utils_unittest.cpp b/media/mtransport/test/runnable_utils_unittest.cpp index b90273b51a12..e1616cce7135 100644 --- a/media/mtransport/test/runnable_utils_unittest.cpp +++ b/media/mtransport/test/runnable_utils_unittest.cpp @@ -1,4 +1,3 @@ - /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public @@ -15,6 +14,7 @@ #include "nsXPCOM.h" #include "nsXPCOMGlue.h" +#include "mozilla/RefPtr.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsIIOService.h" @@ -37,6 +37,20 @@ MtransportTestUtils *test_utils; namespace { +class Destructor { + public: + Destructor(bool* destroyed) : destroyed_(destroyed) {} + ~Destructor() { + std::cerr << "Destructor called" << std::endl; + *destroyed_ = true; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor); + + private: + bool *destroyed_; +}; + class TargetClass { public: TargetClass(int *ran) : ran_(ran) {} @@ -59,10 +73,16 @@ class TargetClass { std::cerr << __FUNCTION__ << std::endl; return x; } + void destructor_target(Destructor*) { + } + + void destructor_target_ref(RefPtr destructor) { + } int *ran_; }; + class RunnableArgsTest : public ::testing::Test { public: RunnableArgsTest() : ran_(0), cl_(&ran_){} @@ -84,7 +104,6 @@ class RunnableArgsTest : public ::testing::Test { TargetClass cl_; }; - class DispatchTest : public ::testing::Test { public: DispatchTest() : ran_(0), cl_(&ran_) {} @@ -126,8 +145,6 @@ class DispatchTest : public ::testing::Test { protected: int ran_; TargetClass cl_; - - private: nsCOMPtr target_; }; @@ -183,6 +200,27 @@ TEST_F(DispatchTest, TestNonMethodRet) { ASSERT_EQ(10, z); } +TEST_F(DispatchTest, TestDestructor) { + bool destroyed = false; + RefPtr destructor = new Destructor(&destroyed); + target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target, + destructor), + NS_DISPATCH_SYNC); + ASSERT_FALSE(destroyed); + destructor = nullptr; + ASSERT_TRUE(destroyed); +} + +TEST_F(DispatchTest, TestDestructorRef) { + bool destroyed = false; + RefPtr destructor = new Destructor(&destroyed); + target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target_ref, + destructor), + NS_DISPATCH_SYNC); + ASSERT_FALSE(destroyed); + destructor = nullptr; + ASSERT_TRUE(destroyed); +} } // end of namespace From fd91ff3ee92149ed9087f4d4351cc40102b00803 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Sat, 22 Dec 2012 00:07:08 +0900 Subject: [PATCH 091/217] Bug 823369 Check mozilla::widget::EventFlags size at compile time r=smaug --- widget/nsGUIEvent.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index e123b1ff4354..2f85f4714606 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -564,13 +564,17 @@ public: } private: - inline void SetRawFlags(uint32_t aRawFlags) + typedef uint32_t RawFlags; + + inline void SetRawFlags(RawFlags aRawFlags) { + MOZ_STATIC_ASSERT(sizeof(EventFlags) <= sizeof(RawFlags), + "mozilla::widget::EventFlags must not be bigger than the RawFlags"); memcpy(this, &aRawFlags, sizeof(EventFlags)); } - inline uint32_t GetRawFlags() const + inline RawFlags GetRawFlags() const { - uint32_t result = 0; + RawFlags result = 0; memcpy(&result, this, sizeof(EventFlags)); return result; } From e960579d7cd1cacc11588c4d6482a0123013a92c Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 21 Dec 2012 10:21:44 -0500 Subject: [PATCH 092/217] Bug 807447 - Respect the initial-scale meta-viewport property on initial page load. r=mbrubeck --- mobile/android/chrome/content/browser.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index a5a32890562a..c74355bdf4df 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3564,7 +3564,7 @@ Tab.prototype = { }, /** Update viewport when the metadata changes. */ - updateViewportMetadata: function updateViewportMetadata(aMetadata) { + updateViewportMetadata: function updateViewportMetadata(aMetadata, aInitialLoad) { if (Services.prefs.getBoolPref("browser.ui.zoom.force-user-scalable")) { aMetadata.allowZoom = true; aMetadata.minZoom = aMetadata.maxZoom = NaN; @@ -3580,12 +3580,12 @@ Tab.prototype = { aMetadata.maxZoom *= scaleRatio; ViewportHandler.setMetadataForDocument(this.browser.contentDocument, aMetadata); - this.updateViewportSize(gScreenWidth); + this.updateViewportSize(gScreenWidth, aInitialLoad); this.sendViewportMetadata(); }, /** Update viewport when the metadata or the window size changes. */ - updateViewportSize: function updateViewportSize(aOldScreenWidth) { + updateViewportSize: function updateViewportSize(aOldScreenWidth, aInitialLoad) { // When this function gets called on window resize, we must execute // this.sendViewportUpdate() so that refreshDisplayPort is called. // Ensure that when making changes to this function that code path @@ -3677,7 +3677,7 @@ Tab.prototype = { // within the screen width. Note that "actual content" may be different // with respect to CSS pixels because of the CSS viewport size changing. let zoomScale = (screenW * oldBrowserWidth) / (aOldScreenWidth * viewportW); - let zoom = this.clampZoom(this._zoom * zoomScale); + let zoom = (aInitialLoad && metadata.defaultZoom) ? metadata.defaultZoom : this.clampZoom(this._zoom * zoomScale); this.setResolution(zoom, false); this.setScrollClampingSize(zoom); this.sendViewportUpdate(); @@ -3734,7 +3734,7 @@ Tab.prototype = { // things here before calling updateMetadata. this.setBrowserSize(kDefaultCSSViewportWidth, kDefaultCSSViewportHeight); this.setResolution(gScreenWidth / this.browserWidth, false); - ViewportHandler.updateMetadata(this); + ViewportHandler.updateMetadata(this, true); // Note that if we draw without a display-port, things can go wrong. By the // time we execute this, it's almost certain a display-port has been set via @@ -3757,7 +3757,7 @@ Tab.prototype = { break; case "nsPref:changed": if (aData == "browser.ui.zoom.force-user-scalable") - ViewportHandler.updateMetadata(this); + ViewportHandler.updateMetadata(this, false); break; } }, @@ -5171,7 +5171,7 @@ var ViewportHandler = { let browser = BrowserApp.getBrowserForDocument(document); let tab = BrowserApp.getTabForBrowser(browser); if (tab) - this.updateMetadata(tab); + this.updateMetadata(tab, false); break; } }, @@ -5194,9 +5194,9 @@ var ViewportHandler = { } }, - updateMetadata: function updateMetadata(tab) { + updateMetadata: function updateMetadata(tab, aInitialLoad) { let metadata = this.getViewportMetadata(tab.browser.contentWindow); - tab.updateViewportMetadata(metadata); + tab.updateViewportMetadata(metadata, aInitialLoad); }, /** From ffd7c73b27c7303875751be528a9798178c8b096 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 21 Dec 2012 10:22:16 -0500 Subject: [PATCH 093/217] Bug 666600 - Add a pref to control the default fallback zoom level of pages. r=mfinkle --- mobile/android/app/mobile.js | 3 +++ mobile/android/chrome/content/browser.js | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 11870de6da89..72c01d1f3644 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -44,6 +44,9 @@ pref("toolkit.storage.synchronous", 0); // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density. pref("browser.viewport.scaleRatio", -1); pref("browser.viewport.desktopWidth", 980); +// The default fallback zoom level to render pages at. Set to -1 to fit page; otherwise +// the value is divided by 1000 and clamped to hard-coded min/max scale values. +pref("browser.viewport.defaultZoom", -1); /* allow scrollbars to float above chrome ui */ pref("ui.scrollbarsCanOverlapContent", 1); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index c74355bdf4df..f8b3dba937c8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -5233,6 +5233,8 @@ var ViewportHandler = { let allowZoomStr = windowUtils.getDocumentMetadata("viewport-user-scalable"); let allowZoom = !/^(0|no|false)$/.test(allowZoomStr) && (minScale != maxScale); + let autoSize = true; + if (isNaN(scale) && isNaN(minScale) && isNaN(maxScale) && allowZoomStr == "" && widthStr == "" && heightStr == "") { // Only check for HandheldFriendly if we don't have a viewport meta tag let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); @@ -5242,15 +5244,23 @@ var ViewportHandler = { let doctype = aWindow.document.doctype; if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) return { defaultZoom: 1, autoSize: true, allowZoom: true }; + + let defaultZoom = Services.prefs.getIntPref("browser.viewport.defaultZoom"); + if (defaultZoom >= 0) { + scale = defaultZoom / 1000; + autoSize = false; + } } scale = this.clamp(scale, kViewportMinScale, kViewportMaxScale); minScale = this.clamp(minScale, kViewportMinScale, kViewportMaxScale); maxScale = this.clamp(maxScale, minScale, kViewportMaxScale); - // If initial scale is 1.0 and width is not set, assume width=device-width - let autoSize = (widthStr == "device-width" || - (!widthStr && (heightStr == "device-height" || scale == 1.0))); + if (autoSize) { + // If initial scale is 1.0 and width is not set, assume width=device-width + autoSize = (widthStr == "device-width" || + (!widthStr && (heightStr == "device-height" || scale == 1.0))); + } return { defaultZoom: scale, From 6f6a341ee25a42caaa42373aeac40e7fdbcce812 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Wed, 12 Sep 2012 22:29:30 +0200 Subject: [PATCH 094/217] Fix for bug 815149 (Add support for SOWs and XBL bindings in new DOM bindings). r=bz. --HG-- extra : rebase_source : c24544fd3d1c99651e279e687a07a02c5b994323 --- content/base/src/Makefile.in | 1 + content/base/src/nsINode.cpp | 14 ++++- content/xbl/src/nsBindingManager.cpp | 12 +--- content/xbl/src/nsXBLBinding.cpp | 31 ++++----- dom/base/nsWrapperCache.h | 23 ++++++- dom/base/nsWrapperCacheInlines.h | 10 +++ dom/bindings/BindingUtils.h | 80 +++++++++++++++++++++--- dom/bindings/Codegen.py | 2 +- dom/bindings/DOMJSClass.h | 6 ++ dom/workers/Worker.cpp | 4 +- js/xpconnect/src/xpcpublic.h | 4 +- js/xpconnect/wrappers/AccessCheck.cpp | 5 ++ js/xpconnect/wrappers/WrapperFactory.cpp | 14 +++-- 13 files changed, 158 insertions(+), 48 deletions(-) diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index a4bfcd881966..c9684b58c9fb 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -200,6 +200,7 @@ LOCAL_INCLUDES += \ -I$(topsrcdir)/layout/svg \ -I$(topsrcdir)/layout/xul/base/src \ -I$(topsrcdir)/netwerk/base/src \ + -I$(topsrcdir)/js/xpconnect/wrappers \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT diff --git a/content/base/src/nsINode.cpp b/content/base/src/nsINode.cpp index 0f571c5751bb..430b4696149e 100644 --- a/content/base/src/nsINode.cpp +++ b/content/base/src/nsINode.cpp @@ -102,6 +102,7 @@ #include "nsCSSParser.h" #include "nsHTMLLegendElement.h" #include "nsWrapperCacheInlines.h" +#include "WrapperFactory.h" using namespace mozilla; using namespace mozilla::dom; @@ -2352,7 +2353,18 @@ nsINode::WrapObject(JSContext *aCx, JSObject *aScope, bool *aTriedToWrap) return nullptr; } - return WrapNode(aCx, aScope, aTriedToWrap); + JSObject* obj = WrapNode(aCx, aScope, aTriedToWrap); + if (obj && ChromeOnlyAccess()) { + // Create a new wrapper and cache it. + JSAutoCompartment ac(aCx, obj); + JSObject* wrapper = xpc::WrapperFactory::WrapSOWObject(aCx, obj); + if (!wrapper) { + ClearWrapper(); + return nullptr; + } + dom::SetSystemOnlyWrapper(obj, this, *wrapper); + } + return obj; } bool diff --git a/content/xbl/src/nsBindingManager.cpp b/content/xbl/src/nsBindingManager.cpp index 92ec13cda7c0..31fff12c1295 100644 --- a/content/xbl/src/nsBindingManager.cpp +++ b/content/xbl/src/nsBindingManager.cpp @@ -1219,17 +1219,7 @@ nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID, nsIXPConnect *xpConnect = nsContentUtils::XPConnect(); - nsCOMPtr wrapper; - xpConnect->GetWrappedNativeOfNativeObject(jscontext, - global->GetGlobalJSObject(), - aContent, - NS_GET_IID(nsISupports), - getter_AddRefs(wrapper)); - NS_ENSURE_TRUE(wrapper, NS_NOINTERFACE); - - JSObject* jsobj = nullptr; - - wrapper->GetJSObject(&jsobj); + JSObject* jsobj = aContent->GetWrapper(); NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE); nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, jscontext, diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 20f4b86e601d..f0ee9e630246 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -59,9 +59,11 @@ #include "nsDOMClassInfo.h" #include "nsJSUtils.h" +#include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/Element.h" using namespace mozilla; +using namespace mozilla::dom; // Helper classes @@ -112,6 +114,11 @@ ValueHasISupportsPrivate(const JS::Value &v) return false; } + const DOMClass* domClass = GetDOMClass(&v.toObject()); + if (domClass) { + return domClass->mDOMObjectIsISupports; + } + JSClass* clasp = ::JS_GetClass(&v.toObject()); const uint32_t HAS_PRIVATE_NSISUPPORTS = JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS; @@ -138,9 +145,9 @@ InstallXBLField(JSContext* cx, // But there are some cases where we must accept |thisObj| but not install a // property on it, or otherwise touch it. Hence this split of |this|-vetting // duties. - nsCOMPtr xpcWrapper = - do_QueryInterface(static_cast(::JS_GetPrivate(thisObj))); - if (!xpcWrapper) { + nsISupports* native = + nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, thisObj); + if (!native) { // Looks like whatever |thisObj| is it's not our nsIContent. It might well // be the proto our binding installed, however, where the private is the // nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception @@ -150,7 +157,7 @@ InstallXBLField(JSContext* cx, return true; } - nsCOMPtr xblNode = do_QueryWrappedNative(xpcWrapper); + nsCOMPtr xblNode = do_QueryInterface(native); if (!xblNode) { xpc::Throw(cx, NS_ERROR_UNEXPECTED); return false; @@ -1208,21 +1215,7 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen nsCxPusher pusher; pusher.Push(cx); - nsCOMPtr wrapper; - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - nsresult rv = - xpc->GetWrappedNativeOfNativeObject(cx, scope, mBoundElement, - NS_GET_IID(nsISupports), - getter_AddRefs(wrapper)); - if (NS_FAILED(rv)) - return; - - JSObject* scriptObject; - if (wrapper) - wrapper->GetJSObject(&scriptObject); - else - scriptObject = nullptr; - + JSObject* scriptObject = mBoundElement->GetWrapper(); if (scriptObject) { // XXX Stay in sync! What if a layered binding has an // ?! diff --git a/dom/base/nsWrapperCache.h b/dom/base/nsWrapperCache.h index 280024146da5..06bba15f6966 100644 --- a/dom/base/nsWrapperCache.h +++ b/dom/base/nsWrapperCache.h @@ -136,6 +136,19 @@ public: return (mWrapperPtrBits & WRAPPER_IS_DOM_BINDING) != 0; } + void SetHasSystemOnlyWrapper() + { + MOZ_ASSERT(GetWrapperPreserveColor(), + "This flag should be set after wrapper creation."); + MOZ_ASSERT(IsDOMBinding(), + "This flag should only be set for DOM bindings."); + mWrapperPtrBits |= WRAPPER_HAS_SOW; + } + + bool HasSystemOnlyWrapper() const + { + return (mWrapperPtrBits & WRAPPER_HAS_SOW) != 0; + } /** * Wrap the object corresponding to this wrapper cache. If non-null is @@ -204,7 +217,15 @@ private: */ enum { WRAPPER_IS_DOM_BINDING = 1 << 1 }; - enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING) }; + /** + * If this bit is set then the wrapper for the native object is a DOM binding + * (regular JS object or proxy) that has a system only wrapper for same-origin + * access. + */ + enum { WRAPPER_HAS_SOW = 1 << 2 }; + + enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING | + WRAPPER_HAS_SOW) }; uintptr_t mWrapperPtrBits; }; diff --git a/dom/base/nsWrapperCacheInlines.h b/dom/base/nsWrapperCacheInlines.h index 2959deba5a36..c56b724de9ac 100644 --- a/dom/base/nsWrapperCacheInlines.h +++ b/dom/base/nsWrapperCacheInlines.h @@ -9,6 +9,16 @@ #include "nsWrapperCache.h" #include "xpcpublic.h" +// We want to encode 3 bits into mWrapperPtrBits, so anything we store in it +// needs to be aligned on 8 byte boundaries. +// JS arenas are aligned on 4k boundaries and padded so that the array of +// JSObjects ends on the end of the arena. If the size of a JSObject is a +// multiple of 8 then the start of every JSObject in an arena should be aligned +// on 8 byte boundaries. +MOZ_STATIC_ASSERT(sizeof(js::shadow::Object) % 8 == 0 && sizeof(JS::Value) == 8, + "We want to rely on JSObject being aligned on 8 byte " + "boundaries."); + inline JSObject* nsWrapperCache::GetWrapper() const { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 43fd25ecdd6d..d2765b666c96 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -475,6 +475,67 @@ CouldBeDOMBinding(nsWrapperCache* aCache) return aCache->IsDOMBinding(); } +// The DOM_OBJECT_SLOT_SOW slot contains a JS::ObjectValue which points to the +// cached system object wrapper (SOW) or JS::UndefinedValue if this class +// doesn't need SOWs. + +inline const JS::Value& +GetSystemOnlyWrapperSlot(JSObject* obj) +{ + MOZ_ASSERT(IsDOMClass(js::GetObjectJSClass(obj)) && + !(js::GetObjectJSClass(obj)->flags & JSCLASS_DOM_GLOBAL)); + return js::GetReservedSlot(obj, DOM_OBJECT_SLOT_SOW); +} +inline void +SetSystemOnlyWrapperSlot(JSObject* obj, const JS::Value& v) +{ + MOZ_ASSERT(IsDOMClass(js::GetObjectJSClass(obj)) && + !(js::GetObjectJSClass(obj)->flags & JSCLASS_DOM_GLOBAL)); + js::SetReservedSlot(obj, DOM_OBJECT_SLOT_SOW, v); +} + +inline bool +GetSameCompartmentWrapperForDOMBinding(JSObject*& obj) +{ + js::Class* clasp = js::GetObjectClass(obj); + if (dom::IsDOMClass(clasp)) { + if (!(clasp->flags & JSCLASS_DOM_GLOBAL)) { + JS::Value v = GetSystemOnlyWrapperSlot(obj); + if (v.isObject()) { + obj = &v.toObject(); + } + } + return true; + } + return IsDOMProxy(obj, clasp); +} + +inline void +SetSystemOnlyWrapper(JSObject* obj, nsWrapperCache* cache, JSObject& wrapper) +{ + SetSystemOnlyWrapperSlot(obj, JS::ObjectValue(wrapper)); + cache->SetHasSystemOnlyWrapper(); +} + +static inline void +WrapNewBindingForSameCompartment(JSContext* cx, JSObject* obj, void* value, + JS::Value* vp) +{ + *vp = JS::ObjectValue(*obj); +} + +static inline void +WrapNewBindingForSameCompartment(JSContext* cx, JSObject* obj, + nsWrapperCache* value, JS::Value* vp) +{ + if (value->HasSystemOnlyWrapper()) { + *vp = GetSystemOnlyWrapperSlot(obj); + MOZ_ASSERT(vp->isObject()); + } else { + *vp = JS::ObjectValue(*obj); + } +} + // Create a JSObject wrapping "value", if there isn't one already, and store it // in *vp. "value" must be a concrete class that implements a // GetWrapperPreserveColor() which can return its existing wrapper, if any, and @@ -485,15 +546,12 @@ MOZ_ALWAYS_INLINE bool WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp) { JSObject* obj = value->GetWrapperPreserveColor(); + bool couldBeDOMBinding = CouldBeDOMBinding(value); if (obj) { xpc_UnmarkNonNullGrayObject(obj); - if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) { - *vp = JS::ObjectValue(*obj); - return true; - } } else { // Inline this here while we have non-dom objects in wrapper caches. - if (!CouldBeDOMBinding(value)) { + if (!couldBeDOMBinding) { return false; } @@ -523,7 +581,6 @@ WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp) MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports, IsISupports::Value); MOZ_ASSERT(CheckWrapperCacheCast::Check()); } -#endif // When called via XrayWrapper, we end up here while running in the // chrome compartment. But the obj we have would be created in @@ -531,8 +588,17 @@ WrapNewBindingObject(JSContext* cx, JSObject* scope, T* value, JS::Value* vp) // make sure it's correctly wrapped for the compartment of |scope|. // cx should already be in the compartment of |scope| here. MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx)); +#endif + + bool sameCompartment = + js::GetObjectCompartment(obj) == js::GetContextCompartment(cx); + if (sameCompartment && couldBeDOMBinding) { + WrapNewBindingForSameCompartment(cx, obj, value, vp); + return true; + } + *vp = JS::ObjectValue(*obj); - return JS_WrapValue(cx, vp); + return (sameCompartment && IS_SLIM_WRAPPER(obj)) || JS_WrapValue(cx, vp); } // Create a JSObject wrapping "value", for cases when "value" is a diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 3fdc1b514551..ce7284e21276 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -156,7 +156,7 @@ class CGDOMJSClass(CGThing): return """ DOMJSClass Class = { { "%s", - JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2), + JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3), %s, /* addProperty */ JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* getProperty */ diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 65fe48df7231..2c0d1442f728 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -23,6 +23,12 @@ class nsCycleCollectionParticipant; // bindings. #define DOM_XRAY_EXPANDO_SLOT 1 +// We use slot 2 for holding either a JS::ObjectValue which points to the cached +// SOW or JS::UndefinedValue if this class doesn't need SOWs. This is not safe +// for globals until bug 760095 is fixed, so that bug blocks converting Window +// to new bindings. +#define DOM_OBJECT_SLOT_SOW 2 + // All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. #define DOM_PROTOTYPE_SLOT JSCLASS_GLOBAL_SLOT_COUNT diff --git a/dom/workers/Worker.cpp b/dom/workers/Worker.cpp index 859f29b4bccb..5ec2bb2441fa 100644 --- a/dom/workers/Worker.cpp +++ b/dom/workers/Worker.cpp @@ -278,7 +278,7 @@ private: DOMJSClass Worker::sClass = { { "Worker", - JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2) | + JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3) | JSCLASS_IMPLEMENTS_BARRIERS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, @@ -395,7 +395,7 @@ private: DOMJSClass ChromeWorker::sClass = { { "ChromeWorker", - JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2) | + JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3) | JSCLASS_IMPLEMENTS_BARRIERS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index c4a02ea590e5..71c553174f7a 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -112,8 +112,8 @@ xpc_FastGetCachedWrapper(nsWrapperCache *cache, JSObject *scope, jsval *vp) "Should never have a slim wrapper when IsDOMBinding()"); if (wrapper && js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(scope) && - (IS_SLIM_WRAPPER(wrapper) || cache->IsDOMBinding() || - xpc_OkToHandOutWrapper(cache))) { + (cache->IsDOMBinding() ? !cache->HasSystemOnlyWrapper() : + (IS_SLIM_WRAPPER(wrapper) || xpc_OkToHandOutWrapper(cache)))) { *vp = OBJECT_TO_JSVAL(wrapper); return wrapper; } diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index aac68f80fdfa..c8bdb3c0fd09 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -20,6 +20,7 @@ #include "FilteringWrapper.h" #include "jsfriendapi.h" +#include "mozilla/dom/BindingUtils.h" using namespace mozilla; using namespace js; @@ -237,6 +238,10 @@ AccessCheck::isSystemOnlyAccessPermitted(JSContext *cx) bool AccessCheck::needsSystemOnlyWrapper(JSObject *obj) { + JSObject* wrapper = obj; + if (dom::GetSameCompartmentWrapperForDOMBinding(wrapper)) + return wrapper != obj; + if (!IS_WN_WRAPPER(obj)) return false; diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 7f86bbfed034..5b3aff3a5cdc 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -19,6 +19,7 @@ #include "mozilla/Likely.h" using namespace js; +using namespace mozilla; namespace xpc { @@ -466,11 +467,16 @@ WrapperFactory::Rewrap(JSContext *cx, JSObject *existing, JSObject *obj, JSObject * WrapperFactory::WrapForSameCompartment(JSContext *cx, JSObject *obj) { - // Only WNs have same-compartment wrappers. - // // NB: The contract of WrapForSameCompartment says that |obj| may or may not - // be a security wrapper. This check implicitly handles the security wrapper - // case. + // be a security wrapper. These checks implicitly handle the security + // wrapper case. + + if (dom::GetSameCompartmentWrapperForDOMBinding(obj)) { + return obj; + } + + MOZ_ASSERT(!dom::IsDOMObject(obj)); + if (!IS_WN_WRAPPER(obj)) return obj; From 893e85fa56788d4806c62557ba7eb58449832435 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Wed, 26 Sep 2012 16:17:46 +0200 Subject: [PATCH 095/217] Fix for bug 820577 (Support reparenting for new DOM binding objects). r=bholley/bz. --HG-- extra : rebase_source : 66dad56b8f543790ffa7e595ccd684c4673403df --- content/base/src/nsNodeUtils.cpp | 23 ++-- dom/bindings/BindingUtils.cpp | 164 +++++++++++++++++++++++++- dom/bindings/BindingUtils.h | 25 ++++ dom/bindings/Codegen.py | 31 ++--- dom/bindings/DOMJSClass.h | 6 + js/xpconnect/src/XPCWrappedNative.cpp | 82 +++++++------ js/xpconnect/src/xpcprivate.h | 1 - 7 files changed, 268 insertions(+), 64 deletions(-) diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index 01d8bc5303ff..249310f4efad 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -30,6 +30,7 @@ #include "nsWrapperCacheInlines.h" #include "nsObjectLoadingContent.h" #include "nsDOMMutationObserver.h" +#include "mozilla/dom/BindingUtils.h" using namespace mozilla::dom; @@ -400,7 +401,9 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, nsresult rv; JSObject *wrapper; - if (aCx && (wrapper = aNode->GetWrapper())) { + bool isDOMBinding; + if (aCx && (wrapper = aNode->GetWrapper()) && + !(isDOMBinding = IsDOMObject(wrapper))) { rv = xpc_MorphSlimWrapper(aCx, aNode); NS_ENSURE_SUCCESS(rv, rv); } @@ -525,15 +528,19 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, } if (aCx && wrapper) { - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - if (xpc) { - rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode); - if (NS_FAILED(rv)) { - aNode->mNodeInfo.swap(nodeInfo); - - return rv; + if (isDOMBinding) { + rv = ReparentWrapper(aCx, wrapper); + } else { + nsIXPConnect *xpc = nsContentUtils::XPConnect(); + if (xpc) { + rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode); } } + if (NS_FAILED(rv)) { + aNode->mNodeInfo.swap(nodeInfo); + + return rv; + } } } diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 3ac4d910c85a..c858eac9d256 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -12,11 +12,12 @@ #include "BindingUtils.h" #include "AccessCheck.h" +#include "nsContentUtils.h" +#include "nsIXPConnect.h" #include "WrapperFactory.h" #include "xpcprivate.h" -#include "nsContentUtils.h" #include "XPCQuickStubs.h" -#include "nsIXPConnect.h" +#include "XrayWrapper.h" namespace mozilla { namespace dom { @@ -1254,5 +1255,164 @@ NativeToString(JSContext* cx, JSObject* wrapper, JSObject* obj, const char* pre, return JS_WrapValue(cx, v); } +// Dynamically ensure that two objects don't end up with the same reserved slot. +class AutoCloneDOMObjectSlotGuard NS_STACK_CLASS +{ +public: + AutoCloneDOMObjectSlotGuard(JSObject* aOld, JSObject* aNew) + : mOldReflector(aOld), mNewReflector(aNew) + { + MOZ_ASSERT(js::GetReservedSlot(aOld, DOM_OBJECT_SLOT) == + js::GetReservedSlot(aNew, DOM_OBJECT_SLOT)); + } + + ~AutoCloneDOMObjectSlotGuard() + { + if (js::GetReservedSlot(mOldReflector, DOM_OBJECT_SLOT).toPrivate()) { + js::SetReservedSlot(mNewReflector, DOM_OBJECT_SLOT, + JS::PrivateValue(nullptr)); + } + } + +private: + JSObject* mOldReflector; + JSObject* mNewReflector; + size_t mSlot; +}; + +nsresult +ReparentWrapper(JSContext* aCx, JSObject* aObj) +{ + const DOMClass* domClass = GetDOMClass(aObj); + + JSObject* oldParent = JS_GetParent(aObj); + JSObject* newParent = domClass->mGetParent(aCx, aObj); + + JSAutoCompartment oldAc(aCx, oldParent); + + if (js::GetObjectCompartment(oldParent) == + js::GetObjectCompartment(newParent)) { + if (!JS_SetParent(aCx, aObj, newParent)) { + MOZ_CRASH(); + } + return NS_OK; + } + + nsISupports* native; + if (!UnwrapDOMObjectToISupports(aObj, native)) { + return NS_OK; + } + + // Before proceeding, eagerly create any same-compartment security wrappers + // that the object might have. This forces us to take the 'WithWrapper' path + // while transplanting that handles this stuff correctly. + JSObject* ww = xpc::WrapperFactory::WrapForSameCompartment(aCx, aObj); + if (!ww) { + return NS_ERROR_FAILURE; + } + + JSAutoCompartment newAc(aCx, newParent); + + // First we clone the reflector. We get a copy of its properties and clone its + // expando chain. The only part that is dangerous here is that if we have to + // return early we must avoid ending up with two reflectors pointing to the + // same native. Other than that, the objects we create will just go away. + + JSObject *proto = + (domClass->mGetProto)(aCx, + js::GetGlobalForObjectCrossCompartment(newParent)); + if (!proto) { + return NS_ERROR_FAILURE; + } + + JSObject *newobj = JS_CloneObject(aCx, aObj, proto, newParent); + if (!newobj) { + return NS_ERROR_FAILURE; + } + + js::SetReservedSlot(newobj, DOM_OBJECT_SLOT, + js::GetReservedSlot(aObj, DOM_OBJECT_SLOT)); + + // At this point, both |aObj| and |newobj| point to the same native + // which is bad, because one of them will end up being finalized with a + // native it does not own. |cloneGuard| ensures that if we exit before + // clearing |aObj|'s reserved slot the reserved slot of |newobj| will be + // set to null. |aObj| will go away soon, because we swap it with + // another object during the transplant and let that object die. + JSObject *propertyHolder; + { + AutoCloneDOMObjectSlotGuard cloneGuard(aObj, newobj); + + propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr, + newParent); + if (!propertyHolder) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!JS_CopyPropertiesFrom(aCx, propertyHolder, aObj)) { + return NS_ERROR_FAILURE; + } + + // Expandos from other compartments are attached to the target JS object. + // Copy them over, and let the old ones die a natural death. + SetXrayExpandoChain(newobj, nullptr); + if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) { + return NS_ERROR_FAILURE; + } + + // We've set up |newobj|, so we make it own the native by nulling + // out the reserved slot of |obj|. + // + // NB: It's important to do this _after_ copying the properties to + // propertyHolder. Otherwise, an object with |foo.x === foo| will + // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x. + js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); + } + + nsWrapperCache* cache = nullptr; + CallQueryInterface(native, &cache); + if (ww != aObj) { + MOZ_ASSERT(cache->HasSystemOnlyWrapper()); + + JSObject *newwrapper = + xpc::WrapperFactory::WrapSOWObject(aCx, newobj); + if (!newwrapper) { + MOZ_CRASH(); + } + + // Ok, now we do the special object-plus-wrapper transplant. + ww = xpc::TransplantObjectWithWrapper(aCx, aObj, ww, newobj, newwrapper); + if (!ww) { + MOZ_CRASH(); + } + + aObj = newobj; + SetSystemOnlyWrapperSlot(aObj, JS::ObjectValue(*ww)); + } else { + aObj = xpc::TransplantObject(aCx, aObj, newobj); + if (!aObj) { + MOZ_CRASH(); + } + } + + bool preserving = cache->PreservingWrapper(); + cache->SetPreservingWrapper(false); + cache->SetWrapper(aObj); + cache->SetPreservingWrapper(preserving); + if (!JS_CopyPropertiesFrom(aCx, aObj, propertyHolder)) { + MOZ_CRASH(); + } + + // We might need to call a hook here similar to PostTransplant. + + // Now we can just fix up the parent and return the wrapper + + if (newParent && !JS_SetParent(aCx, aObj, newParent)) { + MOZ_CRASH(); + } + + return NS_OK; +} + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index d2765b666c96..ee05a860991c 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -1050,6 +1050,28 @@ WrapNativeParent(JSContext* cx, JSObject* scope, const T& p) return WrapNativeParent(cx, scope, GetParentPointer(p), GetWrapperCache(p)); } +HAS_MEMBER(GetParentObject) + +template::Value> +struct GetParentObject +{ + static JSObject* Get(JSContext* cx, JSObject* obj) + { + T* native = UnwrapDOMObject(obj); + return WrapNativeParent(cx, obj, native->GetParentObject()); + } +}; + +template +struct GetParentObject +{ + static JSObject* Get(JSContext* cx, JSObject* obj) + { + MOZ_CRASH(); + return nullptr; + } +}; + template static inline JSObject* WrapCallThisObject(JSContext* cx, JSObject* scope, const T& p) @@ -1657,6 +1679,9 @@ bool NativeToString(JSContext* cx, JSObject* wrapper, JSObject* obj, const char* pre, const char* post, JS::Value* v); +nsresult +ReparentWrapper(JSContext* aCx, JSObject* aObj); + } // namespace dom } // namespace mozilla diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index ce7284e21276..a3022be56e67 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -131,13 +131,17 @@ def DOMClass(descriptor): participant = "nullptr" else: participant = "NS_CYCLE_COLLECTION_PARTICIPANT(%s)" % descriptor.nativeType + getParentObject = "GetParentObject<%s>::Get" % descriptor.nativeType return """{ { %s }, %s, %s, + %s, + GetProtoObject, %s }""" % (prototypeChainString, toStringBool(descriptor.nativeOwnership == 'nsisupports'), NativePropertyHooks(descriptor), + getParentObject, participant) class CGDOMJSClass(CGThing): @@ -832,22 +836,21 @@ class CGDeferredFinalize(CGAbstractStaticMethod): def finalizeHook(descriptor, hookName, context): if descriptor.customFinalize: - return """if (self) { - self->%s(%s); -}""" % (hookName, context) - clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else "" - if descriptor.workers: - release = "self->Release();" - elif descriptor.nativeOwnership == 'nsisupports': - release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); + finalize = "self->%s(%s);" % (hookName, context) + else: + finalize = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else "" + if descriptor.workers: + finalize += "self->Release();" + elif descriptor.nativeOwnership == 'nsisupports': + finalize += """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); if (rt) { rt->DeferredRelease(reinterpret_cast(self)); } else { NS_RELEASE(self); }""" - else: - smartPtr = DeferredFinalizeSmartPtr(descriptor) - release = """static bool registered = false; + else: + smartPtr = DeferredFinalizeSmartPtr(descriptor) + finalize += """static bool registered = false; if (!registered) { XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); if (!rt) { @@ -868,7 +871,7 @@ if (!defer) { return; } Take(*defer, self);""" % { 'smartPtr': smartPtr } - return clearWrapper + release + return CGIfWrapper(CGGeneric(finalize), "self") class CGClassFinalizeHook(CGAbstractClassHook): """ @@ -880,7 +883,7 @@ class CGClassFinalizeHook(CGAbstractClassHook): 'void', args) def generate_code(self): - return CGIndenter(CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))).define() + return CGIndenter(finalizeHook(self.descriptor, self.name, self.args[0].name)).define() class CGClassTraceHook(CGAbstractClassHook): """ @@ -6112,7 +6115,7 @@ class CGDOMJSProxyHandler_finalize(ClassMethod): self.descriptor = descriptor def getBody(self): return ("%s self = UnwrapProxy(proxy);\n\n" % (self.descriptor.nativeType + "*") + - finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name)) + finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define()) class CGDOMJSProxyHandler_getElementIfPresent(ClassMethod): def __init__(self, descriptor): diff --git a/dom/bindings/DOMJSClass.h b/dom/bindings/DOMJSClass.h index 2c0d1442f728..c4c013476e99 100644 --- a/dom/bindings/DOMJSClass.h +++ b/dom/bindings/DOMJSClass.h @@ -135,6 +135,9 @@ enum DOMObjectType { eInterfacePrototype }; +typedef JSObject* (*ParentGetter)(JSContext* aCx, JSObject* aObj); +typedef JSObject* (*ProtoGetter)(JSContext* aCx, JSObject* aGlobal); + struct DOMClass { // A list of interfaces that this object implements, in order of decreasing @@ -149,6 +152,9 @@ struct DOMClass const NativePropertyHooks* mNativeHooks; + ParentGetter mGetParent; + ProtoGetter mGetProto; + // This stores the CC participant for the native, null if this class is for a // worker or for a native inheriting from nsISupports (we can get the CC // participant by QI'ing in that case). diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index facdfa4cac97..6a953b2dbc7c 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -27,6 +27,7 @@ using namespace xpc; using namespace mozilla; +using namespace mozilla::dom; bool xpc_OkToHandOutWrapper(nsWrapperCache *cache) @@ -1665,26 +1666,8 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, // // See bug 751995 for more information. -bool -XPCWrappedNative::IsOrphan() -{ - JSObject *parent = js::GetObjectParent(mFlatJSObject); - - // If there's no parent, we've presumably got a global, which can't be an - // orphan by definition. - if (!parent) - return false; - - // If our parent is a cross-compartment wrapper, it has left us behind. - return js::IsCrossCompartmentWrapper(parent); -} - -// Recursively fix up orphans on the parent chain of a wrapper. Note that this -// can cause a wrapper to move even if IsOrphan() is false, since its parent -// might be an orphan, and fixing the parent causes this wrapper to become an -// orphan. -nsresult -XPCWrappedNative::RescueOrphans(XPCCallContext& ccx) +static nsresult +RescueOrphans(XPCCallContext& ccx, JSObject* obj) { // // Even if we're not an orphan at the moment, one of our ancestors might @@ -1698,7 +1681,7 @@ XPCWrappedNative::RescueOrphans(XPCCallContext& ccx) // NB: We pass stopAtOuter=false during the unwrap because Location objects // are parented to outer window proxies. nsresult rv; - JSObject *parentObj = js::GetObjectParent(mFlatJSObject); + JSObject *parentObj = js::GetObjectParent(obj); if (!parentObj) return NS_OK; // Global object. We're done. parentObj = js::UnwrapObject(parentObj, /* stopAtOuter = */ false); @@ -1706,44 +1689,65 @@ XPCWrappedNative::RescueOrphans(XPCCallContext& ccx) // PreCreate may touch dead compartments. js::AutoMaybeTouchDeadCompartments agc(parentObj); + bool isWN = IS_WRAPPER_CLASS(js::GetObjectClass(obj)); + // There's one little nasty twist here. For reasons described in bug 752764, // we nuke SOW-ed objects after transplanting them. This means that nodes // parented to an element (such as XUL elements), can end up with a nuked proxy // in the parent chain, depending on the order of fixup. Because the proxy is // nuked, we can't follow it anywhere. But we _can_ find the new wrapper for - // the underlying native parent, which is exactly what PreCreate does. - // So do that here. + // the underlying native parent. if (MOZ_UNLIKELY(JS_IsDeadWrapper(parentObj))) { - rv = mScriptableInfo->GetCallback()->PreCreate(mIdentity, ccx, - GetScope()->GetGlobalJSObject(), - &parentObj); - NS_ENSURE_SUCCESS(rv, rv); + if (isWN) { + XPCWrappedNative *wn = + static_cast(js::GetObjectPrivate(obj)); + rv = wn->GetScriptableInfo()->GetCallback()->PreCreate(wn->GetIdentityObject(), ccx, + wn->GetScope()->GetGlobalJSObject(), + &parentObj); + NS_ENSURE_SUCCESS(rv, rv); + } else { + MOZ_ASSERT(IsDOMObject(obj)); + const DOMClass* domClass = GetDOMClass(obj); + parentObj = domClass->mGetParent(ccx, obj); + } } // Morph any slim wrappers, lest they confuse us. - MOZ_ASSERT(IS_WRAPPER_CLASS(js::GetObjectClass(parentObj))); - if (IS_SLIM_WRAPPER_OBJECT(parentObj)) { + if (IS_SLIM_WRAPPER(parentObj)) { bool ok = MorphSlimWrapper(ccx, parentObj); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); } - // Get the WN corresponding to the parent, and recursively fix it up. - XPCWrappedNative *parentWrapper = - static_cast(js::GetObjectPrivate(parentObj)); - rv = parentWrapper->RescueOrphans(ccx); + // Recursively fix up orphans on the parent chain. + rv = RescueOrphans(ccx, parentObj); NS_ENSURE_SUCCESS(rv, rv); // Now that we know our parent is in the right place, determine if we've // been orphaned. If not, we have nothing to do. - if (!IsOrphan()) + if (!js::IsCrossCompartmentWrapper(parentObj)) return NS_OK; // We've been orphaned. Find where our parent went, and follow it. - JSObject *parentGhost = js::GetObjectParent(mFlatJSObject); - JSObject *realParent = js::UnwrapObject(parentGhost); - return ReparentWrapperIfFound(ccx, GetObjectScope(parentGhost), - GetObjectScope(realParent), - realParent, mIdentity); + if (isWN) { + JSObject *realParent = js::UnwrapObject(parentObj); + XPCWrappedNative *wn = + static_cast(js::GetObjectPrivate(obj)); + return wn->ReparentWrapperIfFound(ccx, GetObjectScope(parentObj), + GetObjectScope(realParent), + realParent, wn->GetIdentityObject()); + } + + return ReparentWrapper(ccx, obj); +} + +// Recursively fix up orphans on the parent chain of a wrapper. Note that this +// can cause a wrapper to move even if it is not an orphan, since its parent +// might be an orphan and fixing the parent causes this wrapper to become an +// orphan. +nsresult +XPCWrappedNative::RescueOrphans(XPCCallContext& ccx) +{ + return ::RescueOrphans(ccx, mFlatJSObject); } #define IS_TEAROFF_CLASS(clazz) \ diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index d66331916a8c..f5c5bace357f 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2798,7 +2798,6 @@ public: JSObject* aNewParent, nsISupports* aCOMObj); - bool IsOrphan(); nsresult RescueOrphans(XPCCallContext& ccx); void FlatJSObjectFinalized(); From eec2e6f2b2d8f8f4838e93f6fd6fa8ee7084b034 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 20 Dec 2012 17:43:13 -0500 Subject: [PATCH 096/217] Bug 823725 - Remove the Svc.Private getter since it is no longer used; r=rnewman --- services/sync/modules/util.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index 18d42aa8d4aa..cc8dd1b5b667 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -551,16 +551,6 @@ let _sessionCID = Services.appinfo.ID == SEAMONKEY_ID ? XPCOMUtils.defineLazyServiceGetter(Svc, name, contract, iface); }); -// nsIPrivateBrowsingService is not implemented in mobile Firefox. -// Svc.Private should just return undefined in this case instead of throwing. -XPCOMUtils.defineLazyGetter(Svc, "Private", function() { - try { - return Cc["@mozilla.org/privatebrowsing;1"].getService(Ci["nsIPrivateBrowsingService"]); - } catch (e) { - return undefined; - } -}); - Svc.__defineGetter__("Crypto", function() { let cryptoSvc; let ns = {}; From fdf9730043a6379baa692b61fbb3308b47e66431 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 20 Dec 2012 17:50:35 -0500 Subject: [PATCH 097/217] Bug 823732 - Remove the usage of the global PB service from nsSessionStartup.js in per-window PB builds; r=jdm --- browser/components/sessionstore/src/Makefile.in | 3 ++- browser/components/sessionstore/src/nsSessionStartup.js | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/browser/components/sessionstore/src/Makefile.in b/browser/components/sessionstore/src/Makefile.in index 9ef556269dd1..81ef19f98699 100644 --- a/browser/components/sessionstore/src/Makefile.in +++ b/browser/components/sessionstore/src/Makefile.in @@ -13,9 +13,10 @@ include $(topsrcdir)/config/config.mk EXTRA_COMPONENTS = \ nsSessionStore.manifest \ nsSessionStore.js \ - nsSessionStartup.js \ $(NULL) +EXTRA_PP_COMPONENTS := nsSessionStartup.js + JS_MODULES_PATH := $(FINAL_TARGET)/modules/sessionstore EXTRA_JS_MODULES := \ diff --git a/browser/components/sessionstore/src/nsSessionStartup.js b/browser/components/sessionstore/src/nsSessionStartup.js index b0d6da45203f..93d12f54702d 100644 --- a/browser/components/sessionstore/src/nsSessionStartup.js +++ b/browser/components/sessionstore/src/nsSessionStartup.js @@ -73,11 +73,15 @@ SessionStartup.prototype = { init: function sss_init() { debug("init starting"); // do not need to initialize anything in auto-started private browsing sessions + if (PrivateBrowsingUtils.permanentPrivateBrowsing) + return; + +#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING let pbs = Cc["@mozilla.org/privatebrowsing;1"]. getService(Ci.nsIPrivateBrowsingService); - if (PrivateBrowsingUtils.permanentPrivateBrowsing || - pbs.lastChangedByCommandLine) + if (pbs.lastChangedByCommandLine) return; +#endif // Session state is unknown until we read the file. this._sessionType = null; _SessionFile.read().then( From 64b18f5f2228de7798c259f3dc33c837b50d9dd3 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Thu, 20 Dec 2012 18:17:44 -0500 Subject: [PATCH 098/217] Bug 822330 - Make the Windows download progress bar in the task bar aware of private downloads; r=jdm --- .../downloads/DownloadTaskbarProgress.jsm | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm b/toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm index c3e6d9118169..803522ffdb96 100644 --- a/toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm +++ b/toolkit/mozapps/downloads/DownloadTaskbarProgress.jsm @@ -109,7 +109,7 @@ var DownloadTaskbarProgressUpdater = this._dm = Cc["@mozilla.org/download-manager;1"]. getService(Ci.nsIDownloadManager); - this._dm.addListener(this); + this._dm.addPrivacyAwareListener(this); this._os = Cc["@mozilla.org/observer-service;1"]. getService(Ci.nsIObserverService); @@ -246,7 +246,7 @@ var DownloadTaskbarProgressUpdater = */ _updateStatus: function DTPU_updateStatus() { - let numActive = this._dm.activeDownloadCount; + let numActive = this._dm.activeDownloadCount + this._dm.activePrivateDownloadCount; let totalSize = 0, totalTransferred = 0; if (numActive == 0) { @@ -256,21 +256,22 @@ var DownloadTaskbarProgressUpdater = let numPaused = 0, numScanning = 0; // Enumerate all active downloads - let downloads = this._dm.activeDownloads; - while (downloads.hasMoreElements()) { - let download = downloads.getNext().QueryInterface(Ci.nsIDownload); - // Only set values if we actually know the download size - if (download.percentComplete != -1) { - totalSize += download.size; - totalTransferred += download.amountTransferred; + [this._dm.activeDownloads, this._dm.activePrivateDownloads].forEach(function(downloads) { + while (downloads.hasMoreElements()) { + let download = downloads.getNext().QueryInterface(Ci.nsIDownload); + // Only set values if we actually know the download size + if (download.percentComplete != -1) { + totalSize += download.size; + totalTransferred += download.amountTransferred; + } + // We might need to display a paused state, so track this + if (download.state == this._dm.DOWNLOAD_PAUSED) { + numPaused++; + } else if (download.state == this._dm.DOWNLOAD_SCANNING) { + numScanning++; + } } - // We might need to display a paused state, so track this - if (download.state == this._dm.DOWNLOAD_PAUSED) { - numPaused++; - } else if (download.state == this._dm.DOWNLOAD_SCANNING) { - numScanning++; - } - } + }.bind(this)); // If all downloads are paused, show the progress as paused, unless we // don't have any information about sizes, in which case we don't From b1a1349ba8cce85091c4987eb4e63e967a0d2f35 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 21 Dec 2012 16:01:51 +0000 Subject: [PATCH 099/217] Bug 766883 - Don't abort during shutdown when SignalTracerThread is called twice before FireAndWaitForTracerEvent notices; r=ted --- widget/cocoa/WidgetTraceEvent.mm | 7 ++++--- widget/gtk2/WidgetTraceEvent.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/widget/cocoa/WidgetTraceEvent.mm b/widget/cocoa/WidgetTraceEvent.mm index 15af9f287b8d..c43b39c122c7 100644 --- a/widget/cocoa/WidgetTraceEvent.mm +++ b/widget/cocoa/WidgetTraceEvent.mm @@ -44,9 +44,10 @@ void SignalTracerThread() if (!sMutex || !sCondVar) return; MutexAutoLock lock(*sMutex); - NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong"); - sTracerProcessed = true; - sCondVar->Notify(); + if (!sTracerProcessed) { + sTracerProcessed = true; + sCondVar->Notify(); + } } // This function is called from the background tracer thread. diff --git a/widget/gtk2/WidgetTraceEvent.cpp b/widget/gtk2/WidgetTraceEvent.cpp index b05c341843d0..e99c312abdd4 100644 --- a/widget/gtk2/WidgetTraceEvent.cpp +++ b/widget/gtk2/WidgetTraceEvent.cpp @@ -69,9 +69,10 @@ void SignalTracerThread() if (!sMutex || !sCondVar) return; MutexAutoLock lock(*sMutex); - NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong"); - sTracerProcessed = true; - sCondVar->Notify(); + if (!sTracerProcessed) { + sTracerProcessed = true; + sCondVar->Notify(); + } } } // namespace mozilla From ad4f2ce15941df0b00b576b831155d8491716b69 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 21 Dec 2012 16:09:52 +0000 Subject: [PATCH 100/217] Backout 913296ba049f (bug 766883) since no author was set CLOSED TREE --- widget/cocoa/WidgetTraceEvent.mm | 7 +++---- widget/gtk2/WidgetTraceEvent.cpp | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/widget/cocoa/WidgetTraceEvent.mm b/widget/cocoa/WidgetTraceEvent.mm index c43b39c122c7..15af9f287b8d 100644 --- a/widget/cocoa/WidgetTraceEvent.mm +++ b/widget/cocoa/WidgetTraceEvent.mm @@ -44,10 +44,9 @@ void SignalTracerThread() if (!sMutex || !sCondVar) return; MutexAutoLock lock(*sMutex); - if (!sTracerProcessed) { - sTracerProcessed = true; - sCondVar->Notify(); - } + NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong"); + sTracerProcessed = true; + sCondVar->Notify(); } // This function is called from the background tracer thread. diff --git a/widget/gtk2/WidgetTraceEvent.cpp b/widget/gtk2/WidgetTraceEvent.cpp index e99c312abdd4..b05c341843d0 100644 --- a/widget/gtk2/WidgetTraceEvent.cpp +++ b/widget/gtk2/WidgetTraceEvent.cpp @@ -69,10 +69,9 @@ void SignalTracerThread() if (!sMutex || !sCondVar) return; MutexAutoLock lock(*sMutex); - if (!sTracerProcessed) { - sTracerProcessed = true; - sCondVar->Notify(); - } + NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong"); + sTracerProcessed = true; + sCondVar->Notify(); } } // namespace mozilla From ef4b429105951a6393146b37741241793af70c0c Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 21 Dec 2012 16:11:24 +0000 Subject: [PATCH 101/217] Bug 766883 - Don't abort during shutdown when SignalTracerThread is called twice before FireAndWaitForTracerEvent notices; r=ted DONTBUILD CLOSED TREE --- widget/cocoa/WidgetTraceEvent.mm | 7 ++++--- widget/gtk2/WidgetTraceEvent.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/widget/cocoa/WidgetTraceEvent.mm b/widget/cocoa/WidgetTraceEvent.mm index 15af9f287b8d..c43b39c122c7 100644 --- a/widget/cocoa/WidgetTraceEvent.mm +++ b/widget/cocoa/WidgetTraceEvent.mm @@ -44,9 +44,10 @@ void SignalTracerThread() if (!sMutex || !sCondVar) return; MutexAutoLock lock(*sMutex); - NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong"); - sTracerProcessed = true; - sCondVar->Notify(); + if (!sTracerProcessed) { + sTracerProcessed = true; + sCondVar->Notify(); + } } // This function is called from the background tracer thread. diff --git a/widget/gtk2/WidgetTraceEvent.cpp b/widget/gtk2/WidgetTraceEvent.cpp index b05c341843d0..e99c312abdd4 100644 --- a/widget/gtk2/WidgetTraceEvent.cpp +++ b/widget/gtk2/WidgetTraceEvent.cpp @@ -69,9 +69,10 @@ void SignalTracerThread() if (!sMutex || !sCondVar) return; MutexAutoLock lock(*sMutex); - NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong"); - sTracerProcessed = true; - sCondVar->Notify(); + if (!sTracerProcessed) { + sTracerProcessed = true; + sCondVar->Notify(); + } } } // namespace mozilla From 020839f9bbdc85f3cba131af6f78da6d89f90e36 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Fri, 21 Dec 2012 16:15:01 +0000 Subject: [PATCH 102/217] Backout 21409a401d75 (bug 821292),9587e39f9a50 (bug 820102) for conflicts and assertions respectively, on a CLOSED TREE --- media/mtransport/databuffer.h | 46 --- media/mtransport/runnable_utils.h | 22 +- .../test/runnable_utils_unittest.cpp | 46 +-- .../signaling/src/media/VcmSIPCCBinding.cpp | 80 ++---- .../src/mediapipeline/MediaPipeline.cpp | 265 +++++++----------- .../src/mediapipeline/MediaPipeline.h | 233 ++++++--------- .../src/peerconnection/PeerConnectionCtx.cpp | 2 - .../src/peerconnection/PeerConnectionImpl.cpp | 29 +- .../src/peerconnection/PeerConnectionImpl.h | 4 +- .../peerconnection/PeerConnectionMedia.cpp | 23 ++ .../src/peerconnection/PeerConnectionMedia.h | 28 +- .../signaling/test/mediapipeline_unittest.cpp | 27 +- 12 files changed, 294 insertions(+), 511 deletions(-) delete mode 100644 media/mtransport/databuffer.h diff --git a/media/mtransport/databuffer.h b/media/mtransport/databuffer.h deleted file mode 100644 index f8f890b7f9a9..000000000000 --- a/media/mtransport/databuffer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* 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/. */ - -// Original author: ekr@rtfm.com - -#ifndef databuffer_h__ -#define databuffer_h__ -#include -#include -#include - -namespace mozilla { - -class DataBuffer { - public: - DataBuffer() : data_(nullptr), len_(0) {} - DataBuffer(const uint8_t *data, size_t len) { - Assign(data, len); - } - - void Assign(const uint8_t *data, size_t len) { - data_ = new unsigned char[ len ? len : 1]; // Don't depend on new [0]. - memcpy(static_cast(data_.get()), - static_cast(data), len); - len_ = len; - } - - const uint8_t *data() const { return data_; } - size_t len() const { return len_; } - const bool empty() const { return len_ != 0; } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataBuffer) - -private: - ScopedDeleteArray data_; - size_t len_; - - DISALLOW_COPY_ASSIGN(DataBuffer); -}; - -} - -#endif diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h index e45d2ddfe58a..09d0715ac953 100644 --- a/media/mtransport/runnable_utils.h +++ b/media/mtransport/runnable_utils.h @@ -10,13 +10,13 @@ #define runnable_utils_h__ #include "nsThreadUtils.h" -#include "mozilla/RefPtr.h" // Abstract base class for all of our templates namespace mozilla { class runnable_args_base : public nsRunnable { public: + NS_IMETHOD Run() = 0; }; @@ -39,25 +39,7 @@ class runnable_args_base : public nsRunnable { #include "runnable_utils_generated.h" // Temporary hack. Really we want to have a template which will do this -static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, nsIRunnable *runnable, uint32_t flags) { - RefPtr runnable_ref(runnable); - - if (thread && (thread != nsRefPtr(do_GetCurrentThread()))) { - return thread->Dispatch(runnable_ref, flags); - } - - return runnable_ref->Run(); -} - -#define ASSERT_ON_THREAD(t) do { \ - if (t) { \ - bool on; \ - nsresult rv; \ - rv = t->IsOnCurrentThread(&on); \ - MOZ_ASSERT(NS_SUCCEEDED(rv)); \ - MOZ_ASSERT(on); \ - } \ - } while(0) +#define RUN_ON_THREAD(t, r, h) ((t && (t != nsRefPtr(do_GetCurrentThread()))) ? t->Dispatch(r, h) : r->Run()) } #endif diff --git a/media/mtransport/test/runnable_utils_unittest.cpp b/media/mtransport/test/runnable_utils_unittest.cpp index e1616cce7135..b90273b51a12 100644 --- a/media/mtransport/test/runnable_utils_unittest.cpp +++ b/media/mtransport/test/runnable_utils_unittest.cpp @@ -1,3 +1,4 @@ + /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public @@ -14,7 +15,6 @@ #include "nsXPCOM.h" #include "nsXPCOMGlue.h" -#include "mozilla/RefPtr.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsIIOService.h" @@ -37,20 +37,6 @@ MtransportTestUtils *test_utils; namespace { -class Destructor { - public: - Destructor(bool* destroyed) : destroyed_(destroyed) {} - ~Destructor() { - std::cerr << "Destructor called" << std::endl; - *destroyed_ = true; - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor); - - private: - bool *destroyed_; -}; - class TargetClass { public: TargetClass(int *ran) : ran_(ran) {} @@ -73,16 +59,10 @@ class TargetClass { std::cerr << __FUNCTION__ << std::endl; return x; } - void destructor_target(Destructor*) { - } - - void destructor_target_ref(RefPtr destructor) { - } int *ran_; }; - class RunnableArgsTest : public ::testing::Test { public: RunnableArgsTest() : ran_(0), cl_(&ran_){} @@ -104,6 +84,7 @@ class RunnableArgsTest : public ::testing::Test { TargetClass cl_; }; + class DispatchTest : public ::testing::Test { public: DispatchTest() : ran_(0), cl_(&ran_) {} @@ -145,6 +126,8 @@ class DispatchTest : public ::testing::Test { protected: int ran_; TargetClass cl_; + + private: nsCOMPtr target_; }; @@ -200,27 +183,6 @@ TEST_F(DispatchTest, TestNonMethodRet) { ASSERT_EQ(10, z); } -TEST_F(DispatchTest, TestDestructor) { - bool destroyed = false; - RefPtr destructor = new Destructor(&destroyed); - target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target, - destructor), - NS_DISPATCH_SYNC); - ASSERT_FALSE(destroyed); - destructor = nullptr; - ASSERT_TRUE(destroyed); -} - -TEST_F(DispatchTest, TestDestructorRef) { - bool destroyed = false; - RefPtr destructor = new Destructor(&destroyed); - target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target_ref, - destructor), - NS_DISPATCH_SYNC); - ASSERT_FALSE(destroyed); - destructor = nullptr; - ASSERT_TRUE(destroyed); -} } // end of namespace diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp index d8a17bdfd0c6..defc3d6307bd 100644 --- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp +++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp @@ -909,7 +909,7 @@ static short vcmCreateRemoteStream_m( hints |= nsDOMMediaStream::HINT_CONTENTS_VIDEO; } - nsRefPtr info; + sipcc::RemoteSourceStreamInfo* info; res = pc.impl()->CreateRemoteSourceStreamInfo(hints, &info); if (NS_FAILED(res)) { return VCM_ERROR; @@ -1329,26 +1329,14 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id, if (conduit->ConfigureRecvMediaCodecs(configs)) return VCM_ERROR; - // Now we have all the pieces, create the pipeline - mozilla::RefPtr pipeline = + stream->StorePipeline(pc_track_id, new mozilla::MediaPipelineReceiveAudio( - pc.impl()->GetHandle(), pc.impl()->GetMainThread().get(), pc.impl()->GetSTSThread(), - stream->GetMediaStream()->GetStream(), - conduit, rtp_flow, rtcp_flow); + stream->GetMediaStream(), + conduit, rtp_flow, rtcp_flow)); - nsresult res = pipeline->Init(); - if (NS_FAILED(res)) { - CSFLogError(logTag, "Failure initializing audio pipeline"); - return VCM_ERROR; - } - - CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", - pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); - - stream->StorePipeline(pc_track_id, pipeline); } else if (CC_IS_VIDEO(mcap_id)) { std::vector configs; @@ -1374,24 +1362,13 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id, return VCM_ERROR; // Now we have all the pieces, create the pipeline - mozilla::RefPtr pipeline = - new mozilla::MediaPipelineReceiveVideo( - pc.impl()->GetHandle(), - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream()->GetStream(), - conduit, rtp_flow, rtcp_flow); + stream->StorePipeline(pc_track_id, + new mozilla::MediaPipelineReceiveVideo( + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream(), + conduit, rtp_flow, rtcp_flow)); - nsresult res = pipeline->Init(); - if (NS_FAILED(res)) { - CSFLogError(logTag, "Failure initializing video pipeline"); - return VCM_ERROR; - } - - CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", - pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); - - stream->StorePipeline(pc_track_id, pipeline); } else { CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__); return VCM_ERROR; @@ -1959,23 +1936,16 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id, if (!conduit || conduit->ConfigureSendMediaCodec(config)) return VCM_ERROR; - mozilla::RefPtr pipeline = - new mozilla::MediaPipelineTransmit( - pc.impl()->GetHandle(), - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream()->GetStream(), - conduit, rtp_flow, rtcp_flow); + mozilla::RefPtr pipeline = + new mozilla::MediaPipelineTransmit( + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream(), + conduit, rtp_flow, rtcp_flow); - nsresult res = pipeline->Init(); - if (NS_FAILED(res)) { - CSFLogError(logTag, "Failure initializing audio pipeline"); - return VCM_ERROR; - } CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); - // Now we have all the pieces, create the pipeline stream->StorePipeline(pc_track_id, pipeline); @@ -1998,24 +1968,18 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id, if (!conduit || conduit->ConfigureSendMediaCodec(config)) return VCM_ERROR; - // Now we have all the pieces, create the pipeline + // Create the pipeline mozilla::RefPtr pipeline = new mozilla::MediaPipelineTransmit( - pc.impl()->GetHandle(), - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream()->GetStream(), - conduit, rtp_flow, rtcp_flow); - - nsresult res = pipeline->Init(); - if (NS_FAILED(res)) { - CSFLogError(logTag, "Failure initializing video pipeline"); - return VCM_ERROR; - } + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream(), + conduit, rtp_flow, rtcp_flow); CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + // Now we have all the pieces, create the pipeline stream->StorePipeline(pc_track_id, pipeline); } else { CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__); diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index 0efe968ac1ee..b8c90101bf8f 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -23,7 +23,6 @@ #include "nsError.h" #include "AudioSegment.h" #include "MediaSegment.h" -#include "databuffer.h" #include "transportflow.h" #include "transportlayer.h" #include "transportlayerdtls.h" @@ -33,14 +32,6 @@ using namespace mozilla; -#ifdef DEBUG -// Dial up pipeline logging in debug mode -#define MP_LOG_INFO PR_LOG_WARN -#else -#define MP_LOG_INFO PR_LOG_INFO -#endif - - // Logging context MOZ_MTLOG_MODULE("mediapipeline"); @@ -49,7 +40,6 @@ namespace mozilla { static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp"; nsresult MediaPipeline::Init() { - ASSERT_ON_THREAD(main_thread_); conduit_->AttachTransport(transport_); MOZ_ASSERT(rtp_transport_); @@ -62,10 +52,7 @@ nsresult MediaPipeline::Init() { if (rtp_transport_->state() == TransportLayer::TS_OPEN) { res = TransportReady(rtp_transport_); - if (NS_FAILED(res)) { - MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()"); - return res; - } + NS_ENSURE_SUCCESS(res, res); } else { if (!muxed_) { rtcp_transport_->SignalStateChange.connect(this, @@ -73,10 +60,7 @@ nsresult MediaPipeline::Init() { if (rtcp_transport_->state() == TransportLayer::TS_OPEN) { res = TransportReady(rtcp_transport_); - if (NS_FAILED(res)) { - MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()"); - return res; - } + NS_ENSURE_SUCCESS(res, res); } } } @@ -86,9 +70,7 @@ nsresult MediaPipeline::Init() { // Disconnect us from the transport so that we can cleanly destruct // the pipeline on the main thread. -void MediaPipeline::DetachTransport_s() { - ASSERT_ON_THREAD(sts_thread_); - +void MediaPipeline::DetachTransportInt() { transport_->Detach(); rtp_transport_ = NULL; rtcp_transport_ = NULL; @@ -96,13 +78,13 @@ void MediaPipeline::DetachTransport_s() { void MediaPipeline::DetachTransport() { RUN_ON_THREAD(sts_thread_, - WrapRunnable(this, &MediaPipeline::DetachTransport_s), + WrapRunnable(this, &MediaPipeline::DetachTransportInt), NS_DISPATCH_SYNC); } void MediaPipeline::StateChange(TransportFlow *flow, TransportLayer::State state) { if (state == TransportLayer::TS_OPEN) { - MOZ_MTLOG(MP_LOG_INFO, "Flow is ready"); + MOZ_MTLOG(PR_LOG_DEBUG, "Flow is ready"); TransportReady(flow); } else if (state == TransportLayer::TS_CLOSED || state == TransportLayer::TS_ERROR) { @@ -126,21 +108,18 @@ nsresult MediaPipeline::TransportReady(TransportFlow *flow) { } nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { - MOZ_ASSERT(!description_.empty()); bool rtcp = !(flow == rtp_transport_); State *state = rtcp ? &rtcp_state_ : &rtp_state_; if (*state != MP_CONNECTING) { MOZ_MTLOG(PR_LOG_ERROR, "Transport ready for flow in wrong state:" << - description_ << ": " << (rtcp ? "rtcp" : "rtp")); + (rtcp ? "rtcp" : "rtp")); return NS_ERROR_FAILURE; } nsresult res; - MOZ_MTLOG(MP_LOG_INFO, "Transport ready for pipeline " << - static_cast(this) << " flow " << description_ << ": " << - (rtcp ? "rtcp" : "rtp")); + MOZ_MTLOG(PR_LOG_DEBUG, "Transport ready for flow " << (rtcp ? "rtcp" : "rtp")); // Now instantiate the SRTP objects TransportLayerDtls *dtls = static_cast( @@ -213,14 +192,14 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { rtcp_send_srtp_ = rtp_send_srtp_; rtcp_recv_srtp_ = rtp_recv_srtp_; - MOZ_MTLOG(MP_LOG_INFO, "Listening for packets received on " << + MOZ_MTLOG(PR_LOG_DEBUG, "Listening for packets received on " << static_cast(dtls->downward())); dtls->downward()->SignalPacketReceived.connect(this, &MediaPipeline:: PacketReceived); } else { - MOZ_MTLOG(MP_LOG_INFO, "Listening for RTP packets received on " << + MOZ_MTLOG(PR_LOG_DEBUG, "Listening for RTP packets received on " << static_cast(dtls->downward())); dtls->downward()->SignalPacketReceived.connect(this, @@ -240,7 +219,7 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { return NS_ERROR_FAILURE; } - MOZ_MTLOG(MP_LOG_INFO, "Listening for RTCP packets received on " << + MOZ_MTLOG(PR_LOG_DEBUG, "Listening for RTCP packets received on " << static_cast(dtls->downward())); // Start listening @@ -260,7 +239,7 @@ nsresult MediaPipeline::TransportFailed(TransportFlow *flow) { *state = MP_CLOSED; - MOZ_MTLOG(MP_LOG_INFO, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp")); + MOZ_MTLOG(PR_LOG_DEBUG, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp")); NS_WARNING( "MediaPipeline Transport failed. This is not properly cleaned up yet"); @@ -275,10 +254,25 @@ nsresult MediaPipeline::TransportFailed(TransportFlow *flow) { } +// Wrapper to send a packet on the STS thread. nsresult MediaPipeline::SendPacket(TransportFlow *flow, const void *data, int len) { - ASSERT_ON_THREAD(sts_thread_); + nsresult rv; + nsresult res; + rv = RUN_ON_THREAD(sts_thread_, + WrapRunnableRet(this, &MediaPipeline::SendPacketInt, flow, data, len, &res), + NS_DISPATCH_SYNC); + + // res is invalid unless the dispatch succeeded + if (NS_FAILED(rv)) + return rv; + + return res; +} + +nsresult MediaPipeline::SendPacketInt(TransportFlow *flow, const void *data, + int len) { // Note that we bypass the DTLS layer here TransportLayerDtls *dtls = static_cast( flow->GetLayer(TransportLayerDtls::ID())); @@ -301,45 +295,37 @@ nsresult MediaPipeline::SendPacket(TransportFlow *flow, const void *data, void MediaPipeline::increment_rtp_packets_sent() { ++rtp_packets_sent_; - - if (!(rtp_packets_sent_ % 100)) { - MOZ_MTLOG(MP_LOG_INFO, "RTP sent packet count for " << description_ - << " Pipeline " << static_cast(this) - << " Flow : " << static_cast(rtp_transport_) + if (!(rtp_packets_sent_ % 1000)) { + MOZ_MTLOG(PR_LOG_DEBUG, "RTP packet count " << static_cast(this) << ": " << rtp_packets_sent_); } } void MediaPipeline::increment_rtcp_packets_sent() { ++rtcp_packets_sent_; - if (!(rtcp_packets_sent_ % 100)) { - MOZ_MTLOG(MP_LOG_INFO, "RTCP sent packet count for " << description_ - << " Pipeline " << static_cast(this) - << " Flow : " << static_cast(rtcp_transport_) + if (!(rtcp_packets_sent_ % 1000)) { + MOZ_MTLOG(PR_LOG_DEBUG, "RTCP packet count " << static_cast(this) << ": " << rtcp_packets_sent_); } } void MediaPipeline::increment_rtp_packets_received() { ++rtp_packets_received_; - if (!(rtp_packets_received_ % 100)) { - MOZ_MTLOG(MP_LOG_INFO, "RTP received packet count for " << description_ - << " Pipeline " << static_cast(this) - << " Flow : " << static_cast(rtp_transport_) + if (!(rtp_packets_received_ % 1000)) { + MOZ_MTLOG(PR_LOG_DEBUG, "RTP packet count " << static_cast(this) << ": " << rtp_packets_received_); } } void MediaPipeline::increment_rtcp_packets_received() { ++rtcp_packets_received_; - if (!(rtcp_packets_received_ % 100)) { - MOZ_MTLOG(MP_LOG_INFO, "RTCP received packet count for " << description_ - << " Pipeline " << static_cast(this) - << " Flow : " << static_cast(rtcp_transport_) + if (!(rtcp_packets_received_ % 1000)) { + MOZ_MTLOG(PR_LOG_DEBUG, "RTCP packet count " << static_cast(this) << ": " << rtcp_packets_received_); } } + void MediaPipeline::RtpPacketReceived(TransportLayer *layer, const unsigned char *data, size_t len) { @@ -348,10 +334,9 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer, return; } - if (!conduit_) { - MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; media disconnected"); - return; - } + // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived + // TODO(ekr@rtfm.com): filter on SSRC for bundle + increment_rtp_packets_received(); MOZ_ASSERT(rtp_recv_srtp_); // This should never happen @@ -361,10 +346,6 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer, return; } - // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived - // TODO(ekr@rtfm.com): filter on SSRC for bundle - increment_rtp_packets_received(); - // Make a copy rather than cast away constness ScopedDeletePtr inner_data( new unsigned char[len]); @@ -386,17 +367,6 @@ void MediaPipeline::RtcpPacketReceived(TransportLayer *layer, return; } - if (!conduit_) { - MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; media disconnected"); - return; - } - - if (direction_ == RECEIVE) { - // Discard any RTCP that is being transmitted to us - // This will be unnecessary when we have SSRC filtering. - return; - } - increment_rtcp_packets_received(); MOZ_ASSERT(rtcp_recv_srtp_); // This should never happen @@ -464,55 +434,25 @@ void MediaPipeline::PacketReceived(TransportLayer *layer, } nsresult MediaPipelineTransmit::Init() { - ASSERT_ON_THREAD(main_thread_); - - description_ = pc_ + "| "; - description_ += conduit_->type() == MediaSessionConduit::AUDIO ? - "Transmit audio" : "Transmit video"; - // TODO(ekr@rtfm.com): Check for errors MOZ_MTLOG(PR_LOG_DEBUG, "Attaching pipeline to stream " << static_cast(stream_) << " conduit type=" << (conduit_->type() == MediaSessionConduit::AUDIO ? - "audio" : "video")); + "audio" : "video") << + " hints=" << stream_->GetHintContents()); - stream_->AddListener(listener_); - - return MediaPipeline::Init(); -} - -nsresult MediaPipelineTransmit::TransportReady(TransportFlow *flow) { - // Call base ready function. - MediaPipeline::TransportReady(flow); - - if (flow == rtp_transport_) { - // TODO(ekr@rtfm.com): Move onto MSG thread. - listener_->SetActive(true); - } - - return NS_OK; + // Force this to be a refptr so that we are holding a strong reference + // to the media stream. + nsRefPtr stream (stream_->GetStream()); + return RUN_ON_THREAD(main_thread_, WrapRunnable(stream, + &MediaStream::AddListener, + listener_), + NS_DISPATCH_NORMAL); } nsresult MediaPipeline::PipelineTransport::SendRtpPacket( const void *data, int len) { - nsresult ret; - - nsAutoPtr buf(new DataBuffer(static_cast(data), - len)); - - RUN_ON_THREAD(sts_thread_, - WrapRunnableRet( - RefPtr(this), - &MediaPipeline::PipelineTransport::SendRtpPacket_s, - buf, &ret), - NS_DISPATCH_NORMAL); - - return NS_OK; -} - -nsresult MediaPipeline::PipelineTransport::SendRtpPacket_s( - nsAutoPtr data) { if (!pipeline_) return NS_OK; // Detached @@ -527,15 +467,14 @@ nsresult MediaPipeline::PipelineTransport::SendRtpPacket_s( // libsrtp enciphers in place, so we need a new, big enough // buffer. // XXX. allocates and deletes one buffer per packet sent. - // Bug 822129 - int max_len = data->len() + SRTP_MAX_EXPANSION; + int max_len = len + SRTP_MAX_EXPANSION; ScopedDeletePtr inner_data( new unsigned char[max_len]); - memcpy(inner_data, data->data(), data->len()); + memcpy(inner_data, data, len); int out_len; nsresult res = pipeline_->rtp_send_srtp_->ProtectRtp(inner_data, - data->len(), + len, max_len, &out_len); if (!NS_SUCCEEDED(res)) @@ -548,23 +487,6 @@ nsresult MediaPipeline::PipelineTransport::SendRtpPacket_s( nsresult MediaPipeline::PipelineTransport::SendRtcpPacket( const void *data, int len) { - nsresult ret; - - nsAutoPtr buf(new DataBuffer(static_cast(data), - len)); - - RUN_ON_THREAD(sts_thread_, - WrapRunnableRet( - RefPtr(this), - &MediaPipeline::PipelineTransport::SendRtcpPacket_s, - buf, &ret), - NS_DISPATCH_NORMAL); - - return NS_OK; -} - -nsresult MediaPipeline::PipelineTransport::SendRtcpPacket_s( - nsAutoPtr data) { if (!pipeline_) return NS_OK; // Detached @@ -579,15 +501,14 @@ nsresult MediaPipeline::PipelineTransport::SendRtcpPacket_s( // libsrtp enciphers in place, so we need a new, big enough // buffer. // XXX. allocates and deletes one buffer per packet sent. - // Bug 822129. - int max_len = data->len() + SRTP_MAX_EXPANSION; + int max_len = len + SRTP_MAX_EXPANSION; ScopedDeletePtr inner_data( new unsigned char[max_len]); - memcpy(inner_data, data->data(), data->len()); + memcpy(inner_data, data, len); int out_len; nsresult res = pipeline_->rtcp_send_srtp_->ProtectRtcp(inner_data, - data->len(), + len, max_len, &out_len); if (!NS_SUCCEEDED(res)) @@ -604,18 +525,22 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, TrackTicks offset, uint32_t events, const MediaSegment& queued_media) { + if (!pipeline_) + return; // Detached + MOZ_MTLOG(PR_LOG_DEBUG, "MediaPipeline::NotifyQueuedTrackChanges()"); - if (!active_) { - MOZ_MTLOG(PR_LOG_DEBUG, "Discarding packets because transport not ready"); + // Return early if we are not connected to avoid queueing stuff + // up in the conduit + if (pipeline_->rtp_transport_->state() != TransportLayer::TS_OPEN) { + MOZ_MTLOG(PR_LOG_DEBUG, "Transport not ready yet, dropping packets"); return; } // TODO(ekr@rtfm.com): For now assume that we have only one // track type and it's destined for us - // See bug 784517 if (queued_media.GetType() == MediaSegment::AUDIO) { - if (conduit_->type() != MediaSessionConduit::AUDIO) { + if (pipeline_->conduit_->type() != MediaSessionConduit::AUDIO) { // Ignore data in case we have a muxed stream return; } @@ -624,13 +549,14 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, AudioSegment::ChunkIterator iter(*audio); while(!iter.IsEnded()) { - ProcessAudioChunk(static_cast(conduit_.get()), - rate, *iter); + pipeline_->ProcessAudioChunk(static_cast + (pipeline_->conduit_.get()), + rate, *iter); iter.Next(); } } else if (queued_media.GetType() == MediaSegment::VIDEO) { #ifdef MOZILLA_INTERNAL_API - if (conduit_->type() != MediaSessionConduit::VIDEO) { + if (pipeline_->conduit_->type() != MediaSessionConduit::VIDEO) { // Ignore data in case we have a muxed stream return; } @@ -639,8 +565,9 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, VideoSegment::ChunkIterator iter(*video); while(!iter.IsEnded()) { - ProcessVideoChunk(static_cast(conduit_.get()), - rate, *iter); + pipeline_->ProcessVideoChunk(static_cast + (pipeline_->conduit_.get()), + rate, *iter); iter.Next(); } #endif @@ -649,10 +576,9 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, } } -void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk( - AudioSessionConduit *conduit, - TrackRate rate, - AudioChunk& chunk) { +void MediaPipelineTransmit::ProcessAudioChunk(AudioSessionConduit *conduit, + TrackRate rate, + AudioChunk& chunk) { // TODO(ekr@rtfm.com): Do more than one channel nsAutoArrayPtr samples(new int16_t[chunk.mDuration]); @@ -686,10 +612,9 @@ void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk( } #ifdef MOZILLA_INTERNAL_API -void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk( - VideoSessionConduit* conduit, - TrackRate rate, - VideoChunk& chunk) { +void MediaPipelineTransmit::ProcessVideoChunk(VideoSessionConduit *conduit, + TrackRate rate, + VideoChunk& chunk) { // We now need to send the video frame to the other side layers::Image *img = chunk.mFrame.GetImage(); if (!img) { @@ -742,20 +667,27 @@ void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk( #endif nsresult MediaPipelineReceiveAudio::Init() { - ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(PR_LOG_DEBUG, __FUNCTION__); - description_ = pc_ + "| Receive audio"; - - stream_->AddListener(listener_); - - return MediaPipelineReceive::Init(); + // Force this to be a refptr so that we are holding a strong reference + // to the media stream. + nsRefPtr stream (stream_->GetStream()); + return RUN_ON_THREAD(main_thread_, WrapRunnable(stream, + &MediaStream::AddListener, + listener_), + NS_DISPATCH_NORMAL); } void MediaPipelineReceiveAudio::PipelineListener:: NotifyPull(MediaStreamGraph* graph, StreamTime total) { - MOZ_ASSERT(source_); - if (!source_) { + if (!pipeline_) + return; // Detached + + SourceMediaStream *source = + pipeline_->stream_->GetStream()->AsSourceStream(); + + MOZ_ASSERT(source); + if (!source) { MOZ_MTLOG(PR_LOG_ERROR, "NotifyPull() called from a non-SourceMediaStream"); return; } @@ -783,7 +715,7 @@ NotifyPull(MediaStreamGraph* graph, StreamTime total) { int samples_length; MediaConduitErrorCode err = - static_cast(conduit_.get())->GetAudioFrame( + static_cast(pipeline_->conduit_.get())->GetAudioFrame( static_cast(samples->Data()), 16000, // Sampling rate fixed at 16 kHz for now 0, // TODO(ekr@rtfm.com): better estimate of capture delay @@ -799,21 +731,20 @@ NotifyPull(MediaStreamGraph* graph, StreamTime total) { segment.AppendFrames(samples.forget(), samples_length, 0, samples_length, AUDIO_FORMAT_S16); - source_->AppendToTrack(1, // TODO(ekr@rtfm.com): Track ID - &segment); + char buf[32]; + PR_snprintf(buf, 32, "%p", source); + source->AppendToTrack(1, // TODO(ekr@rtfm.com): Track ID + &segment); } } nsresult MediaPipelineReceiveVideo::Init() { - ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(PR_LOG_DEBUG, __FUNCTION__); - description_ = pc_ + "| Receive video"; - static_cast(conduit_.get())-> AttachRenderer(renderer_); - return MediaPipelineReceive::Init(); + return NS_OK; } MediaPipelineReceiveVideo::PipelineRenderer::PipelineRenderer( @@ -824,7 +755,7 @@ MediaPipelineReceiveVideo::PipelineRenderer::PipelineRenderer( #ifdef MOZILLA_INTERNAL_API image_container_ = layers::LayerManager::CreateImageContainer(); SourceMediaStream *source = - pipeline_->stream_->AsSourceStream(); + pipeline_->stream_->GetStream()->AsSourceStream(); source->AddTrack(1 /* Track ID */, 30, 0, new VideoSegment()); source->AdvanceKnownTracksTime(STREAM_TIME_MAX); #endif @@ -837,7 +768,7 @@ void MediaPipelineReceiveVideo::PipelineRenderer::RenderVideoFrame( int64_t render_time) { #ifdef MOZILLA_INTERNAL_API SourceMediaStream *source = - pipeline_->stream_->AsSourceStream(); + pipeline_->stream_->GetStream()->AsSourceStream(); // Create a video frame and append it to the track. ImageFormat format = PLANAR_YCBCR; diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h index 787b75f47995..c1738b78ae89 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -17,8 +17,6 @@ #include "MediaConduitInterface.h" #include "AudioSegment.h" #include "SrtpFlow.h" -#include "databuffer.h" -#include "runnable_utils.h" #include "transportflow.h" #ifdef MOZILLA_INTERNAL_API @@ -37,35 +35,14 @@ namespace mozilla { // network -> transport -> [us] -> conduit -> [us] -> stream -> Playout // // The boxes labeled [us] are just bridge logic implemented in this class -// -// We have to deal with a number of threads: -// -// GSM: -// * Assembles the pipeline -// SocketTransportService -// * Receives notification that ICE and DTLS have completed -// * Processes incoming network data and passes it to the conduit -// * Processes outgoing RTP and RTCP -// MediaStreamGraph -// * Receives outgoing data from the MediaStreamGraph -// * Receives pull requests for more data from the -// MediaStreamGraph -// One or another GIPS threads -// * Receives RTCP messages to send to the other side -// * Processes video frames GIPS wants to render -// -// For a transmitting conduit, "output" is RTP and "input" is RTCP. -// For a receiving conduit, "input" is RTP and "output" is RTCP. -// class MediaPipeline : public sigslot::has_slots<> { public: enum Direction { TRANSMIT, RECEIVE }; enum State { MP_CONNECTING, MP_OPEN, MP_CLOSED }; - MediaPipeline(const std::string& pc, - Direction direction, + MediaPipeline(Direction direction, nsCOMPtr main_thread, nsCOMPtr sts_thread, - MediaStream *stream, + nsDOMMediaStream* stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) @@ -87,31 +64,21 @@ class MediaPipeline : public sigslot::has_slots<> { rtcp_packets_sent_(0), rtp_packets_received_(0), rtcp_packets_received_(0), - muxed_((rtcp_transport_ == NULL) || (rtp_transport_ == rtcp_transport_)), - pc_(pc), - description_() { + muxed_((rtcp_transport_ == NULL) || (rtp_transport_ == rtcp_transport_)) { + Init(); } virtual ~MediaPipeline() { - MOZ_ASSERT(!stream_); // Check that we have shut down already. - } - - void Shutdown() { - ASSERT_ON_THREAD(main_thread_); - // First shut down networking and then disconnect from - // the media streams. DetachTransport() is sync so - // we are sure that the transport is shut down before - // we touch stream_ or conduit_. DetachTransport(); - if (stream_) { - DetachMediaStream(); - } } virtual nsresult Init(); virtual Direction direction() const { return direction_; } + virtual void DetachMediaStream() {} + virtual void DetachTransport(); + int rtp_packets_sent() const { return rtp_packets_sent_; } int rtcp_packets_sent() const { return rtp_packets_sent_; } int rtp_packets_received() const { return rtp_packets_received_; } @@ -120,17 +87,13 @@ class MediaPipeline : public sigslot::has_slots<> { // Thread counting NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline) - protected: - virtual void DetachMediaStream() {} - + protected: // Separate class to allow ref counting class PipelineTransport : public TransportInterface { public: // Implement the TransportInterface functions PipelineTransport(MediaPipeline *pipeline) - : pipeline_(pipeline), - sts_thread_(pipeline->sts_thread_) {} - + : pipeline_(pipeline) {} void Detach() { pipeline_ = NULL; } MediaPipeline *pipeline() const { return pipeline_; } @@ -138,11 +101,7 @@ class MediaPipeline : public sigslot::has_slots<> { virtual nsresult SendRtcpPacket(const void* data, int len); private: - virtual nsresult SendRtpPacket_s(nsAutoPtr data); - virtual nsresult SendRtcpPacket_s(nsAutoPtr data); - MediaPipeline *pipeline_; // Raw pointer to avoid cycles - nsCOMPtr sts_thread_; }; friend class PipelineTransport; @@ -165,51 +124,31 @@ class MediaPipeline : public sigslot::has_slots<> { void PacketReceived(TransportLayer *layer, const unsigned char *data, size_t len); - Direction direction_; - RefPtr stream_; // A pointer to the stream we are servicing. - // Written on the main thread. - // Used on STS and MediaStreamGraph threads. - RefPtr conduit_; // Our conduit. Written on the main - // thread. Read on STS thread. - // The transport objects. Read/written on STS thread. + Direction direction_; + nsDOMMediaStream* stream_; + RefPtr conduit_; RefPtr rtp_transport_; State rtp_state_; RefPtr rtcp_transport_; State rtcp_state_; - - // Pointers to the threads we need. Initialized at creation - // and used all over the place. nsCOMPtr main_thread_; nsCOMPtr sts_thread_; - - // Created on Init. Referenced by the conduit and eventually - // destroyed on the STS thread. RefPtr transport_; - - // Used only on STS thread. + bool transport_connected_; RefPtr rtp_send_srtp_; RefPtr rtcp_send_srtp_; RefPtr rtp_recv_srtp_; RefPtr rtcp_recv_srtp_; - - // Written only on STS thread. May be read on other - // threads but since there is no mutex, the values - // will only be approximate. int rtp_packets_sent_; int rtcp_packets_sent_; int rtp_packets_received_; int rtcp_packets_received_; - - // Written on Init. Read on STS thread. bool muxed_; - std::string pc_; - std::string description_; private: - void DetachTransport(); - void DetachTransport_s(); - + virtual void DetachTransportInt(); + nsresult SendPacketInt(TransportFlow *flow, const void* data, int len); nsresult TransportReadyInt(TransportFlow *flow); bool IsRtp(const unsigned char *data, size_t len); @@ -220,44 +159,47 @@ class MediaPipeline : public sigslot::has_slots<> { // and transmitting to the network. class MediaPipelineTransmit : public MediaPipeline { public: - MediaPipelineTransmit(const std::string& pc, - nsCOMPtr main_thread, + MediaPipelineTransmit(nsCOMPtr main_thread, nsCOMPtr sts_thread, - MediaStream *stream, + nsDOMMediaStream* stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipeline(pc, TRANSMIT, main_thread, sts_thread, + MediaPipeline(TRANSMIT, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), - listener_(new PipelineListener(conduit)) {} - - // Initialize (stuff here may fail) - virtual nsresult Init(); - - // Called on the main thread. - virtual void DetachMediaStream() { - ASSERT_ON_THREAD(main_thread_); - stream_->RemoveListener(listener_); - // Remove our reference so that when the MediaStreamGraph - // releases the listener, it will be destroyed. - listener_ = nullptr; - stream_ = nullptr; + listener_(new PipelineListener(this)) { + Init(); // TODO(ekr@rtfm.com): ignoring error } - // Override MediaPipeline::TransportReady. - virtual nsresult TransportReady(TransportFlow *flow); + // Initialize (stuff here may fail) + nsresult Init(); + + virtual ~MediaPipelineTransmit() { + if (stream_ && listener_){ + stream_->GetStream()->RemoveListener(listener_); + + // These shouldn't be necessary, but just to make sure + // that if we have messed up ownership somehow the + // interfaces just abort. + listener_->Detach(); + } + } + + virtual void DetachMediaStream() { + // TODO(ekr@rtfm.com): Are multiple removes a problem? + stream_->GetStream()->RemoveListener(listener_); + stream_ = NULL; + listener_->Detach(); + } // Separate class to allow ref counting class PipelineListener : public MediaStreamListener { public: - PipelineListener(const RefPtr& conduit) - : conduit_(conduit), active_(false) {} + PipelineListener(MediaPipelineTransmit *pipeline) : + pipeline_(pipeline) {} + void Detach() { pipeline_ = NULL; } - // XXX. This is not thread-safe but the hazard is just - // that active_ = true takes a while to propagate. Revisit - // when 823600 lands. - void SetActive(bool active) { active_ = active; } // Implement MediaStreamListener virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, @@ -268,19 +210,18 @@ class MediaPipelineTransmit : public MediaPipeline { virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {} private: - virtual void ProcessAudioChunk(AudioSessionConduit *conduit, - TrackRate rate, AudioChunk& chunk); -#ifdef MOZILLA_INTERNAL_API - virtual void ProcessVideoChunk(VideoSessionConduit *conduit, - TrackRate rate, VideoChunk& chunk); -#endif - RefPtr conduit_; - volatile bool active_; + MediaPipelineTransmit *pipeline_; // Raw pointer to avoid cycles }; + friend class PipelineListener; private: -RefPtr listener_; - + virtual void ProcessAudioChunk(AudioSessionConduit *conduit, + TrackRate rate, AudioChunk& chunk); +#ifdef MOZILLA_INTERNAL_API + virtual void ProcessVideoChunk(VideoSessionConduit *conduit, + TrackRate rate, VideoChunk& chunk); +#endif + RefPtr listener_; }; @@ -288,14 +229,13 @@ RefPtr listener_; // rendering video. class MediaPipelineReceive : public MediaPipeline { public: - MediaPipelineReceive(const std::string& pc, - nsCOMPtr main_thread, + MediaPipelineReceive(nsCOMPtr main_thread, nsCOMPtr sts_thread, - MediaStream *stream, + nsDOMMediaStream* stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipeline(pc, RECEIVE, main_thread, sts_thread, + MediaPipeline(RECEIVE, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), segments_added_(0) { @@ -314,40 +254,41 @@ class MediaPipelineReceive : public MediaPipeline { // rendering audio. class MediaPipelineReceiveAudio : public MediaPipelineReceive { public: - MediaPipelineReceiveAudio(const std::string& pc, - nsCOMPtr main_thread, + MediaPipelineReceiveAudio(nsCOMPtr main_thread, nsCOMPtr sts_thread, - MediaStream *stream, + nsDOMMediaStream* stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipelineReceive(pc, main_thread, sts_thread, + MediaPipelineReceive(main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), - listener_(new PipelineListener(stream->AsSourceStream(), - conduit)) { + listener_(new PipelineListener(this)) { + Init(); + } + + ~MediaPipelineReceiveAudio() { + if (stream_ && listener_) { + stream_->GetStream()->RemoveListener(listener_); + listener_->Detach(); + } } virtual void DetachMediaStream() { - ASSERT_ON_THREAD(main_thread_); - stream_->RemoveListener(listener_); - // Remove our reference so that when the MediaStreamGraph - // releases the listener, it will be destroyed. - listener_ = nullptr; - stream_ = nullptr; + // TODO(ekr@rtfm.com): Are multiple removes a problem? + stream_->GetStream()->RemoveListener(listener_); + stream_ = NULL; + listener_->Detach(); } - virtual nsresult Init(); - private: // Separate class to allow ref counting class PipelineListener : public MediaStreamListener { public: - PipelineListener(SourceMediaStream * source, - const RefPtr& conduit) - : source_(source), - conduit_(conduit), - played_(0) {} + PipelineListener(MediaPipelineReceiveAudio *pipeline) + : pipeline_(pipeline), + played_(0) {} + void Detach() { pipeline_ = NULL; } // Implement MediaStreamListener virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, @@ -358,10 +299,12 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime); private: - SourceMediaStream *source_; - RefPtr conduit_; + MediaPipelineReceiveAudio *pipeline_; // Raw pointer to avoid cycles StreamTime played_; }; + friend class PipelineListener; + + nsresult Init(); RefPtr listener_; }; @@ -371,29 +314,22 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { // rendering video. class MediaPipelineReceiveVideo : public MediaPipelineReceive { public: - MediaPipelineReceiveVideo(const std::string& pc, - nsCOMPtr main_thread, + MediaPipelineReceiveVideo(nsCOMPtr main_thread, nsCOMPtr sts_thread, - MediaStream *stream, + nsDOMMediaStream* stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipelineReceive(pc, main_thread, sts_thread, + MediaPipelineReceive(main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), renderer_(new PipelineRenderer(this)) { + Init(); } - // Called on the main thread. - virtual void DetachMediaStream() { - ASSERT_ON_THREAD(main_thread_); - conduit_ = nullptr; // Force synchronous destruction so we - // stop generating video. - stream_ = nullptr; + ~MediaPipelineReceiveVideo() { } - virtual nsresult Init(); - private: class PipelineRenderer : public VideoRenderer { public: @@ -424,6 +360,7 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive { }; friend class PipelineRenderer; + nsresult Init(); RefPtr renderer_; }; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index cc7e2b72596a..29e13a9089fa 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -31,9 +31,7 @@ nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread) { gMainThread = mainThread; CSF::VcmSIPCCBinding::setMainThread(gMainThread); } else { -#ifdef MOZILLA_INTERNAL_API MOZ_ASSERT(gMainThread == mainThread); -#endif } nsresult res; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index b9930600ff06..55417fe2cc95 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -260,7 +260,19 @@ PeerConnectionImpl::MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval) } nsresult -PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, nsRefPtr* aInfo) +PeerConnectionImpl::MakeRemoteSource(nsDOMMediaStream* aStream, RemoteSourceStreamInfo** aInfo) +{ + MOZ_ASSERT(aInfo); + MOZ_ASSERT(aStream); + + // TODO(ekr@rtfm.com): Add the track info with the first segment + nsRefPtr remote = new RemoteSourceStreamInfo(aStream); + NS_ADDREF(*aInfo = remote); + return NS_OK; +} + +nsresult +PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStreamInfo** aInfo) { MOZ_ASSERT(aInfo); PC_AUTO_ENTER_API_CALL_NO_CHECK(); @@ -276,8 +288,19 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, nsRefPtr(comstream->GetStream())->SetPullEnabled(true); nsRefPtr remote; - remote = new RemoteSourceStreamInfo(comstream); - *aInfo = remote; + if (!mThread || NS_IsMainThread()) { + remote = new RemoteSourceStreamInfo(comstream); + NS_ADDREF(*aInfo = remote); + return NS_OK; + } + + mThread->Dispatch(WrapRunnableNMRet( + &PeerConnectionImpl::MakeRemoteSource, comstream, aInfo, &res + ), NS_DISPATCH_SYNC); + + if (NS_FAILED(res)) { + return res; + } return NS_OK; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index f393a2c16df6..57f9f5231201 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -119,14 +119,14 @@ public: static nsresult ConvertConstraints( const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx); static nsresult MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aStream); + static nsresult MakeRemoteSource(nsDOMMediaStream* aStream, RemoteSourceStreamInfo** aInfo); Role GetRole() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mRole; } - nsresult CreateRemoteSourceStreamInfo(uint32_t aHint, - nsRefPtr* aInfo); + nsresult CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStreamInfo** aInfo); // Implementation of the only observer we need virtual void onCallEvent( diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index 774e849858a6..c7c404e41dbd 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -28,6 +28,22 @@ static const char* logTag = "PeerConnectionMedia"; static const mozilla::TrackID TRACK_AUDIO = 0; static const mozilla::TrackID TRACK_VIDEO = 1; +/* We get this callback in order to find out which tracks are audio and which + * are video. We should get this callback right away for existing streams after + * we add this class as a listener. + */ +void +LocalSourceStreamInfo::NotifyQueuedTrackChanges( + mozilla::MediaStreamGraph* aGraph, + mozilla::TrackID aID, + mozilla::TrackRate aTrackRate, + mozilla::TrackTicks aTrackOffset, + uint32_t aTrackEvents, + const mozilla::MediaSegment& aQueuedMedia) +{ + /* TODO: use this callback to keep track of changes to the MediaStream */ +} + /* If the ExpectAudio hint is on we will add a track at the default first * audio track ID (0) * FIX - Do we need to iterate over the tracks instead of taking these hints? @@ -181,6 +197,13 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream localSourceStream->ExpectVideo(TRACK_VIDEO); } + // Make it the listener for info from the MediaStream and add it to the list + mozilla::MediaStream *plainMediaStream = stream->GetStream(); + + if (plainMediaStream) { + plainMediaStream->AddListener(localSourceStream); + } + mLocalSourceStreams.AppendElement(localSourceStream); PR_Unlock(mLocalSourceStreamsLock); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index 38b76580e379..f46cb8eabf2b 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -157,7 +157,7 @@ class Fake_VideoGenerator { }; #endif -class LocalSourceStreamInfo { +class LocalSourceStreamInfo : public mozilla::MediaStreamListener { public: LocalSourceStreamInfo(nsDOMMediaStream* aMediaStream) : mMediaStream(aMediaStream) {} @@ -165,6 +165,24 @@ public: mMediaStream = NULL; } + /** + * Notify that changes to one of the stream tracks have been queued. + * aTrackEvents can be any combination of TRACK_EVENT_CREATED and + * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track + * at aTrackOffset (relative to the start of the stream). + */ + virtual void NotifyQueuedTrackChanges( + mozilla::MediaStreamGraph* aGraph, + mozilla::TrackID aID, + mozilla::TrackRate aTrackRate, + mozilla::TrackTicks aTrackOffset, + uint32_t aTrackEvents, + const mozilla::MediaSegment& aQueuedMedia + ); + + virtual void NotifyPull(mozilla::MediaStreamGraph* aGraph, + mozilla::StreamTime aDesiredTime) {} + nsDOMMediaStream* GetMediaStream() { return mMediaStream; } @@ -176,16 +194,18 @@ public: unsigned VideoTrackCount(); void Detach() { + // Disconnect my own listener + GetMediaStream()->GetStream()->RemoveListener(this); + // walk through all the MediaPipelines and disconnect them. for (std::map >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { - it->second->Shutdown(); + it->second->DetachMediaStream(); } mMediaStream = NULL; } - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo) private: std::map > mPipelines; nsRefPtr mMediaStream; @@ -209,7 +229,7 @@ class RemoteSourceStreamInfo { for (std::map >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { - it->second->Shutdown(); + it->second->DetachMediaStream(); } mMediaStream = NULL; } diff --git a/media/webrtc/signaling/test/mediapipeline_unittest.cpp b/media/webrtc/signaling/test/mediapipeline_unittest.cpp index 072962a1a3b5..908efe99f082 100644 --- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp +++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp @@ -137,15 +137,9 @@ class TestAgentSend : public TestAgent { ConfigureSendMediaCodec(&audio_config_); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - std::string test_pc("PC"); - - audio_pipeline_ = new mozilla::MediaPipelineTransmit( - test_pc, - NULL, - test_utils->sts_target(), - audio_->GetStream(), audio_conduit_, audio_flow_, NULL); - - audio_pipeline_->Init(); + audio_pipeline_ = new mozilla::MediaPipelineTransmit(NULL, + test_utils->sts_target(), + audio_, audio_conduit_, audio_flow_, NULL); // video_ = new Fake_nsDOMMediaStream(new Fake_VideoStreamSource()); // video_pipeline_ = new mozilla::MediaPipelineTransmit(video_, video_conduit_, &video_flow_, &video_flow_); @@ -176,16 +170,11 @@ class TestAgentReceive : public TestAgent { ConfigureRecvMediaCodecs(codecs); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - std::string test_pc("PC"); - audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio( - test_pc, - NULL, - test_utils->sts_target(), - audio_->GetStream(), - static_cast(audio_conduit_.get()), - audio_flow_, NULL); - - audio_pipeline_->Init(); + audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(NULL, + test_utils->sts_target(), + audio_, + static_cast(audio_conduit_.get()), + audio_flow_, NULL); } private: From 0183064a12a651b85e7713539312c5b25d112a4d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 21 Dec 2012 11:02:06 -0600 Subject: [PATCH 103/217] Bug 823310 - Only report strict warnings when JSOPTION_STRICT is set. r=njn,jorendorff --- js/src/frontend/BytecodeEmitter.cpp | 2 +- js/src/frontend/Parser.h | 3 +-- js/src/frontend/TokenStream.cpp | 5 ++--- js/src/frontend/TokenStream.h | 3 +-- js/src/jit-test/tests/basic/bug823310.js | 7 +++++++ 5 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug823310.js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 6a634765c257..6f94172201f6 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1718,7 +1718,7 @@ BytecodeEmitter::reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = tokenStream()->reportStrictWarningErrorNumberVA(pn, sc->strict, errorNumber, args); + bool result = tokenStream()->reportStrictWarningErrorNumberVA(pn, errorNumber, args); va_end(args); return result; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 510adfb697fc..8b711afe9d67 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -518,8 +518,7 @@ Parser::reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...) { va_list args; va_start(args, errorNumber); - bool result = tokenStream.reportStrictWarningErrorNumberVA(pn, pc->sc->strict, - errorNumber, args); + bool result = tokenStream.reportStrictWarningErrorNumberVA(pn, errorNumber, args); va_end(args); return result; } diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 3336a1e0d3c7..20188f976326 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -612,10 +612,9 @@ TokenStream::reportWarning(unsigned errorNumber, ...) } bool -TokenStream::reportStrictWarningErrorNumberVA(ParseNode *pn, bool strictMode, unsigned errorNumber, - va_list args) +TokenStream::reportStrictWarningErrorNumberVA(ParseNode *pn, unsigned errorNumber, va_list args) { - if (!strictMode && !cx->hasStrictOption()) + if (!cx->hasStrictOption()) return true; return reportCompileErrorNumberVA(NULL, JSREPORT_STRICT | JSREPORT_WARNING, errorNumber, args); diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 50bce890ca4c..eccf0eba2be9 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -541,8 +541,7 @@ class TokenStream va_list args); bool reportStrictModeErrorNumberVA(ParseNode *pn, bool strictMode, unsigned errorNumber, va_list args); - bool reportStrictWarningErrorNumberVA(ParseNode *pn, bool strictMode, unsigned errorNumber, - va_list args); + bool reportStrictWarningErrorNumberVA(ParseNode *pn, unsigned errorNumber, va_list args); private: // These are private because they should only be called by the tokenizer diff --git a/js/src/jit-test/tests/basic/bug823310.js b/js/src/jit-test/tests/basic/bug823310.js new file mode 100644 index 000000000000..6a1d6aafb5b5 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug823310.js @@ -0,0 +1,7 @@ +"use strict"; +options("werror"); + +// This construct causes a strict warning, but we shouldn't get one since +// JSOPTION_STRICT isn't enabled. +var x; +eval("if (x = 3) {}"); From cfb3f3bd1313c7268ada9c6e9868a38185e30f8b Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 21 Dec 2012 11:10:11 -0500 Subject: [PATCH 104/217] Bug 823945 - Port XUL Fennec's LoginManagerChild to use per-window PB APIs; r=jdm --HG-- extra : rebase_source : 4b2656a7fd82004201ba562d6a9c80b5abcaff28 --- .../xul/chrome/content/LoginManagerChild.js | 34 ++++--------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/mobile/xul/chrome/content/LoginManagerChild.js b/mobile/xul/chrome/content/LoginManagerChild.js index 238e912e1c6d..7628fc16f6ef 100644 --- a/mobile/xul/chrome/content/LoginManagerChild.js +++ b/mobile/xul/chrome/content/LoginManagerChild.js @@ -7,6 +7,7 @@ var Ci = Components.interfaces; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); var loginManager = { @@ -19,30 +20,6 @@ var loginManager = { getService(Ci.nsIFormFillController); }, - // Private Browsing Service - // If the service is not available, null will be returned. - __privateBrowsingService : undefined, - get _privateBrowsingService() { - if (this.__privateBrowsingService == undefined) { - if ("@mozilla.org/privatebrowsing;1" in Cc) - this.__privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"]. - getService(Ci.nsIPrivateBrowsingService); - else - this.__privateBrowsingService = null; - } - return this.__privateBrowsingService; - }, - - - // Whether we are in private browsing mode - get _inPrivateBrowsing() { - var pbSvc = this._privateBrowsingService; - if (pbSvc) - return pbSvc.privateBrowsingEnabled; - else - return false; - }, - _nsLoginInfo : null, // Constructor for nsILoginInfo implementation _debug : false, // mirrors signon.debug _remember : true, // mirrors signon.rememberSignons preference @@ -532,7 +509,7 @@ var loginManager = { this.log("_fillDocument processing " + forms.length + " forms on " + doc.documentURI); - var autofillForm = !this._inPrivateBrowsing && + var autofillForm = !PrivateBrowsingUtils.isWindowPrivate(doc.defaultView) && Services.prefs.getBoolPref("signon.autofillForms"); // actionOrigins is a list of each form's action origins for this @@ -609,15 +586,16 @@ var loginManager = { * our stored password. */ _onFormSubmit : function (form) { - if (this._inPrivateBrowsing) { + var doc = form.ownerDocument; + var win = doc.defaultView; + + if (PrivateBrowsingUtils.isWindowPrivate(win)) { // We won't do anything in private browsing mode anyway, // so there's no need to perform further checks. this.log("(form submission ignored in private browsing mode)"); return; } - var doc = form.ownerDocument; - // If password saving is disabled (globally or for host), bail out now. if (!this._remember) return; From 68a13d6a9f1ced69a080b3901f3ce1b762cf8d30 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 21 Dec 2012 11:17:22 -0500 Subject: [PATCH 105/217] Bug 823949 - Port XUL Fennec's controller.js to per-window PB APIs; r=jdm --HG-- extra : rebase_source : b59bc7d8edd7840b16baabed635500ff064cab7e --- mobile/xul/chrome/content/browser.js | 4 ++-- mobile/xul/chrome/content/exceptions.js | 28 ++++++++----------------- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/mobile/xul/chrome/content/browser.js b/mobile/xul/chrome/content/browser.js index a2c9bd498ec9..c2a21538caa5 100644 --- a/mobile/xul/chrome/content/browser.js +++ b/mobile/xul/chrome/content/browser.js @@ -910,9 +910,9 @@ var Browser = { let sslExceptions = new SSLExceptions(); if (json.action == "permanent") - sslExceptions.addPermanentException(uri); + sslExceptions.addPermanentException(uri, errorDoc.defaultView); else - sslExceptions.addTemporaryException(uri); + sslExceptions.addTemporaryException(uri, errorDoc.defaultView); } catch (e) { dump("EXCEPTION handle content command: " + e + "\n" ); } diff --git a/mobile/xul/chrome/content/exceptions.js b/mobile/xul/chrome/content/exceptions.js index 3e04824673d6..0866762705b6 100644 --- a/mobile/xul/chrome/content/exceptions.js +++ b/mobile/xul/chrome/content/exceptions.js @@ -4,6 +4,9 @@ let Cc = Components.classes; let Ci = Components.interfaces; +let Cu = Components.utils; + +Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); /** A class to add exceptions to override SSL certificate problems. The functionality @@ -39,19 +42,6 @@ SSLExceptions.prototype = { return true; // suppress error UI }, - /** - Returns true if the private browsing mode is currently active - */ - _inPrivateBrowsingMode: function SSLE_inPrivateBrowsingMode() { - try { - var pb = Cc["@mozilla.org/privatebrowsing;1"].getService(Ci.nsIPrivateBrowsingService); - return pb.privateBrowsingEnabled; - } catch (ex) { - Components.utils.reportError("Could not get the Private Browsing service"); - } - return false; - }, - /** Attempt to download the certificate for the location specified to get the SSLState for the certificate and the errors. @@ -81,14 +71,14 @@ SSLExceptions.prototype = { /** Internal method to create an override. */ - _addOverride: function SSLE_addOverride(aURI, temporary) { + _addOverride: function SSLE_addOverride(aURI, aWindow, temporary) { var SSLStatus = this._checkCert(aURI); var certificate = SSLStatus.serverCert; var flags = 0; // in private browsing do not store exceptions permanently ever - if (this._inPrivateBrowsingMode()) { + if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) { temporary = true; } @@ -111,15 +101,15 @@ SSLExceptions.prototype = { Creates a permanent exception to override all overridable errors for the given URL. */ - addPermanentException: function SSLE_addPermanentException(aURI) { - this._addOverride(aURI, false); + addPermanentException: function SSLE_addPermanentException(aURI, aWindow) { + this._addOverride(aURI, aWindow, false); }, /** Creates a temporary exception to override all overridable errors for the given URL. */ - addTemporaryException: function SSLE_addTemporaryException(aURI) { - this._addOverride(aURI, true); + addTemporaryException: function SSLE_addTemporaryException(aURI, aWindow) { + this._addOverride(aURI, aWindow, true); } }; From fbf5889d742c7f250bb5dd76dddd016250d5e942 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 5 Dec 2012 02:33:20 -0500 Subject: [PATCH 106/217] Bug 814247 - Add auth cache jars for separate apps. r=mayhemer --- .../mochitest/browserElement_Auth.js | 84 +++++++++++++-- dom/plugins/base/nsNPAPIPlugin.cpp | 6 +- netwerk/protocol/http/nsHttpAuthCache.cpp | 101 ++++++++++++++++-- netwerk/protocol/http/nsHttpAuthCache.h | 26 ++++- netwerk/protocol/http/nsHttpAuthManager.cpp | 24 ++++- .../http/nsHttpChannelAuthProvider.cpp | 51 ++++++++- netwerk/protocol/http/nsIHttpAuthManager.idl | 22 +++- netwerk/test/unit/test_auth_jar.js | 52 +++++++++ netwerk/test/unit/xpcshell.ini | 1 + 9 files changed, 341 insertions(+), 26 deletions(-) create mode 100644 netwerk/test/unit/test_auth_jar.js diff --git a/dom/browser-element/mochitest/browserElement_Auth.js b/dom/browser-element/mochitest/browserElement_Auth.js index a3c59a67f00e..1a1b7fb86819 100644 --- a/dom/browser-element/mochitest/browserElement_Auth.js +++ b/dom/browser-element/mochitest/browserElement_Auth.js @@ -40,7 +40,7 @@ function testHttpAuthCancel(e) { iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail); - is(e.detail, 'http auth failed'); + is(e.detail, 'http auth failed', 'expected authentication to fail'); iframe.addEventListener('mozbrowserusernameandpasswordrequired', testHttpAuth); SimpleTest.executeSoon(function() { // Use absolute path because we need to specify host. @@ -48,8 +48,8 @@ function testHttpAuthCancel(e) { }); }); - is(e.detail.realm, 'http_realm'); - is(e.detail.host, 'http://test'); + is(e.detail.realm, 'http_realm', 'expected realm matches'); + is(e.detail.host, 'http://test', 'expected host matches'); e.preventDefault(); SimpleTest.executeSoon(function() { @@ -66,12 +66,12 @@ function testHttpAuth(e) { iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail); - is(e.detail, 'http auth success'); - SimpleTest.executeSoon(testFinish); + is(e.detail, 'http auth success', 'expect authentication to succeed'); + SimpleTest.executeSoon(testAuthJarNoInterfere); }); - is(e.detail.realm, 'http_realm'); - is(e.detail.host, 'http://test'); + is(e.detail.realm, 'http_realm', 'expected realm matches'); + is(e.detail.host, 'http://test', 'expected host matches'); e.preventDefault(); SimpleTest.executeSoon(function() { @@ -79,7 +79,77 @@ function testHttpAuth(e) { }); } +function testAuthJarNoInterfere(e) { + var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1'] + .getService(SpecialPowers.Ci.nsIHttpAuthManager); + var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null); + + // Set a bunch of auth data that should not conflict with the correct auth data already + // stored in the cache. + var principal = secMan.getAppCodebasePrincipal(uri, 1, false); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + principal = secMan.getAppCodebasePrincipal(uri, 1, true); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, false); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + + // Will authenticate with correct password, prompt should not be + // called again. + iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail); + iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { + iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); + iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail); + is(e.detail, 'http auth success', 'expected authentication success'); + SimpleTest.executeSoon(testAuthJarInterfere); + }); + + // Once more with feeling. Ensure that our new auth data doesn't interfere with this mozbrowser's + // auth data. + iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs'; +} + +function testAuthJarInterfere(e) { + var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1'] + .getService(SpecialPowers.Ci.nsIHttpAuthManager); + var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null); + + // Set some auth data that should overwrite the successful stored details. + var principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, true); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + + // Will authenticate with correct password, prompt should not be + // called again. + iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFinish); + iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { + iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); + iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFinish); + SimpleTest.execute(testFail); + }); + + // Once more with feeling. Ensure that our new auth data interferes with this mozbrowser's + // auth data. + iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs'; +} + function testFinish() { + iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFinish); + // Clear login information stored in password manager. var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1'] .getService(SpecialPowers.Ci.nsIHttpAuthManager); diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 75a1b980feca..ebd79381a3cc 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -2771,12 +2771,16 @@ _getauthenticationinfo(NPP instance, const char *protocol, const char *host, bool authPrivate = false; GetPrivacyFromNPP(instance, &authPrivate); + nsIDocument *doc = GetDocumentFromNPP(instance); + NS_ENSURE_TRUE(doc, NPERR_GENERIC_ERROR); + nsIPrincipal *principal = doc->NodePrincipal(); + nsAutoString unused, uname16, pwd16; if (NS_FAILED(authManager->GetAuthIdentity(proto, nsDependentCString(host), port, nsDependentCString(scheme), nsDependentCString(realm), EmptyCString(), unused, uname16, - pwd16, authPrivate))) { + pwd16, authPrivate, principal))) { return NPERR_GENERIC_ERROR; } diff --git a/netwerk/protocol/http/nsHttpAuthCache.cpp b/netwerk/protocol/http/nsHttpAuthCache.cpp index 3d34506bcf84..0109f8238ed5 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.cpp +++ b/netwerk/protocol/http/nsHttpAuthCache.cpp @@ -9,11 +9,20 @@ #include "nsString.h" #include "nsCRT.h" #include "prprf.h" +#include "mozIApplicationClearPrivateDataParams.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "nsNetUtil.h" static inline void -GetAuthKey(const char *scheme, const char *host, int32_t port, nsCString &key) +GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key) { - key.Assign(scheme); + key.Truncate(); + key.AppendInt(appId); + key.Append(':'); + key.AppendInt(inBrowserElement); + key.Append(':'); + key.Append(scheme); key.AppendLiteral("://"); key.Append(host); key.Append(':'); @@ -41,13 +50,23 @@ StrEquivalent(const PRUnichar *a, const PRUnichar *b) nsHttpAuthCache::nsHttpAuthCache() : mDB(nullptr) + , mObserver(new AppDataClearObserver(this)) { + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->AddObserver(mObserver, "webapps-clear-data", false); + } } nsHttpAuthCache::~nsHttpAuthCache() { if (mDB) ClearAll(); + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->RemoveObserver(mObserver, "webapps-clear-data"); + mObserver->mOwner = nullptr; + } } nsresult @@ -71,13 +90,15 @@ nsHttpAuthCache::GetAuthEntryForPath(const char *scheme, const char *host, int32_t port, const char *path, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry) { LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n", scheme, host, port, path)); nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); if (!node) return NS_ERROR_NOT_AVAILABLE; @@ -90,6 +111,8 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, const char *host, int32_t port, const char *realm, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry) { @@ -97,7 +120,7 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, scheme, host, port, realm)); nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); if (!node) return NS_ERROR_NOT_AVAILABLE; @@ -113,6 +136,8 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, const char *realm, const char *creds, const char *challenge, + uint32_t appId, + bool inBrowserElement, const nsHttpAuthIdentity *ident, nsISupports *metadata) { @@ -127,7 +152,7 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, } nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); if (!node) { // create a new entry node and set the given entry @@ -149,13 +174,15 @@ void nsHttpAuthCache::ClearAuthEntry(const char *scheme, const char *host, int32_t port, - const char *realm) + const char *realm, + uint32_t appId, + bool inBrowserElement) { if (!mDB) return; nsAutoCString key; - GetAuthKey(scheme, host, port, key); + GetAuthKey(scheme, host, port, appId, inBrowserElement, key); PL_HashTableRemove(mDB, key.get()); } @@ -179,12 +206,14 @@ nsHttpAuthNode * nsHttpAuthCache::LookupAuthNode(const char *scheme, const char *host, int32_t port, + uint32_t appId, + bool inBrowserElement, nsCString &key) { if (!mDB) return nullptr; - GetAuthKey(scheme, host, port, key); + GetAuthKey(scheme, host, port, appId, inBrowserElement, key); return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get()); } @@ -232,6 +261,62 @@ PLHashAllocOps nsHttpAuthCache::gHashAllocOps = nsHttpAuthCache::FreeEntry }; +NS_IMPL_ISUPPORTS1(nsHttpAuthCache::AppDataClearObserver, nsIObserver) + +NS_IMETHODIMP +nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject, + const char * topic, + const PRUnichar * data_unicode) +{ + NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE); + + nsCOMPtr params = + do_QueryInterface(subject); + if (!params) { + NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); + return NS_ERROR_UNEXPECTED; + } + + uint32_t appId; + bool browserOnly; + + nsresult rv = params->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + rv = params->GetBrowserOnly(&browserOnly); + NS_ENSURE_SUCCESS(rv, rv); + + MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID); + mOwner->ClearAppData(appId, browserOnly); + return NS_OK; +} + +static int +RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg) +{ + nsDependentCString key(static_cast(entry->key)); + nsAutoCString* prefix = static_cast(arg); + if (StringBeginsWith(key, *prefix)) { + return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE; + } + return HT_ENUMERATE_NEXT; +} + +void +nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly) +{ + if (!mDB) { + return; + } + nsAutoCString keyPrefix; + keyPrefix.AppendInt(appId); + keyPrefix.Append(':'); + if (browserOnly) { + keyPrefix.AppendInt(browserOnly); + keyPrefix.Append(':'); + } + PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix); +} + //----------------------------------------------------------------------------- // nsHttpAuthIdentity //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpAuthCache.h b/netwerk/protocol/http/nsHttpAuthCache.h index 7e371190df39..62960f96c593 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.h +++ b/netwerk/protocol/http/nsHttpAuthCache.h @@ -15,6 +15,8 @@ #include "nsCOMPtr.h" #include "plhash.h" #include "nsCRT.h" +#include "nsIObserver.h" + struct nsHttpAuthPath { @@ -180,6 +182,8 @@ public: const char *host, int32_t port, const char *path, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry); // |scheme|, |host|, and |port| are required @@ -189,6 +193,8 @@ public: const char *host, int32_t port, const char *realm, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry); // |scheme|, |host|, and |port| are required @@ -203,13 +209,17 @@ public: const char *realm, const char *credentials, const char *challenge, + uint32_t appId, + bool inBrowserElement, const nsHttpAuthIdentity *ident, nsISupports *metadata); void ClearAuthEntry(const char *scheme, const char *host, int32_t port, - const char *realm); + const char *realm, + uint32_t appId, + bool inBrowserElement); // expire all existing auth list entries including proxy auths. nsresult ClearAll(); @@ -218,6 +228,8 @@ private: nsHttpAuthNode *LookupAuthNode(const char *scheme, const char *host, int32_t port, + uint32_t appId, + bool inBrowserElement, nsCString &key); // hash table allocation functions @@ -227,9 +239,21 @@ private: static void FreeEntry(void *, PLHashEntry *he, unsigned flag); static PLHashAllocOps gHashAllocOps; + + class AppDataClearObserver : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + AppDataClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {} + virtual ~AppDataClearObserver() {} + nsHttpAuthCache* mOwner; + }; + + void ClearAppData(uint32_t appId, bool browserOnly); private: PLHashTable *mDB; // "host:port" --> nsHttpAuthNode + nsRefPtr mObserver; }; #endif // nsHttpAuthCache_h__ diff --git a/netwerk/protocol/http/nsHttpAuthManager.cpp b/netwerk/protocol/http/nsHttpAuthManager.cpp index 0e3cfca1d36e..2ad7d12680ba 100644 --- a/netwerk/protocol/http/nsHttpAuthManager.cpp +++ b/netwerk/protocol/http/nsHttpAuthManager.cpp @@ -8,6 +8,7 @@ #include "nsHttpAuthManager.h" #include "nsReadableUtils.h" #include "nsNetUtil.h" +#include "nsIPrincipal.h" NS_IMPL_ISUPPORTS1(nsHttpAuthManager, nsIHttpAuthManager) @@ -56,22 +57,32 @@ nsHttpAuthManager::GetAuthIdentity(const nsACString & aScheme, nsAString & aUserDomain, nsAString & aUserName, nsAString & aUserPassword, - bool aIsPrivate) + bool aIsPrivate, + nsIPrincipal* aPrincipal) { nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache; nsHttpAuthEntry * entry = nullptr; nsresult rv; + uint32_t appId = NECKO_NO_APP_ID; + bool inBrowserElement = false; + if (aPrincipal) { + appId = aPrincipal->GetAppId(); + inBrowserElement = aPrincipal->GetIsInBrowserElement(); + } + if (!aPath.IsEmpty()) rv = auth_cache->GetAuthEntryForPath(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), aPort, PromiseFlatCString(aPath).get(), + appId, inBrowserElement, &entry); else rv = auth_cache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), aPort, PromiseFlatCString(aRealm).get(), + appId, inBrowserElement, &entry); if (NS_FAILED(rv)) @@ -95,12 +106,20 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme, const nsAString & aUserDomain, const nsAString & aUserName, const nsAString & aUserPassword, - bool aIsPrivate) + bool aIsPrivate, + nsIPrincipal* aPrincipal) { nsHttpAuthIdentity ident(PromiseFlatString(aUserDomain).get(), PromiseFlatString(aUserName).get(), PromiseFlatString(aUserPassword).get()); + uint32_t appId = NECKO_NO_APP_ID; + bool inBrowserElement = false; + if (aPrincipal) { + appId = aPrincipal->GetAppId(); + inBrowserElement = aPrincipal->GetIsInBrowserElement(); + } + nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache; return auth_cache->SetAuthEntry(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), @@ -109,6 +128,7 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme, PromiseFlatCString(aRealm).get(), nullptr, // credentials nullptr, // challenge + appId, inBrowserElement, &ident, nullptr); // metadata } diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index 84db6717c2fe..e6a3ed018702 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -20,6 +20,23 @@ #include "nsIDNSService.h" #include "nsNetCID.h" #include "nsIDNSRecord.h" +#include "nsNetUtil.h" + +static void +GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem) +{ + nsCOMPtr loadContext; + if (aChan) { + NS_QueryNotificationCallbacks(aChan, loadContext); + } + if (!loadContext) { + *aAppId = NECKO_NO_APP_ID; + *aInBrowserElem = false; + } else { + loadContext->GetAppId(aAppId); + loadContext->GetIsInBrowserElement(aInBrowserElem); + } +} nsHttpChannelAuthProvider::nsHttpChannelAuthProvider() : mAuthChannel(nullptr) @@ -372,6 +389,11 @@ nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, // this getter never fails nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + // create a cache entry. we do this even though we don't yet know that // these credentials are valid b/c we need to avoid prompting the user // more than once in case the credentials are valid. @@ -381,6 +403,7 @@ nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, rv = authCache->SetAuthEntry(scheme, host, port, directory, realm, saveCreds ? *result : nullptr, saveChallenge ? challenge : nullptr, + appId, isInBrowserElement, saveIdentity ? &ident : nullptr, sessionState); return rv; @@ -694,6 +717,11 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, return NS_ERROR_NOT_AVAILABLE; } + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + // // if we already tried some credentials for this transaction, then // we need to possibly clear them from the cache, unless the credentials @@ -702,7 +730,8 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, - realm.get(), &entry); + realm.get(), appId, + isInBrowserElement, &entry); // hold reference to the auth session state (in case we clear our // reference to the entry). @@ -732,7 +761,8 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // ok, we've already tried this user identity, so clear the // corresponding entry from the auth cache. authCache->ClearAuthEntry(scheme.get(), host, - port, realm.get()); + port, realm.get(), + appId, isInBrowserElement); entry = nullptr; ident->Clear(); } @@ -1057,10 +1087,17 @@ NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext, nsAutoCString realm; ParseRealm(mCurrentChallenge.get(), realm); + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, - realm.get(), &entry); + realm.get(), appId, + isInBrowserElement, + &entry); nsCOMPtr sessionStateGrip; if (entry) @@ -1292,7 +1329,13 @@ nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache *authCache, continuationState = &mAuthContinuationState; } - rv = authCache->GetAuthEntryForPath(scheme, host, port, path, &entry); + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + + rv = authCache->GetAuthEntryForPath(scheme, host, port, path, + appId, isInBrowserElement, &entry); if (NS_SUCCEEDED(rv)) { // if we are trying to add a header for origin server auth and if the // URL contains an explicit username, then try the given username first. diff --git a/netwerk/protocol/http/nsIHttpAuthManager.idl b/netwerk/protocol/http/nsIHttpAuthManager.idl index 5ec0495e8cfe..582ea60b614d 100644 --- a/netwerk/protocol/http/nsIHttpAuthManager.idl +++ b/netwerk/protocol/http/nsIHttpAuthManager.idl @@ -5,6 +5,8 @@ #include "nsISupports.idl" +interface nsIPrincipal; + /** * nsIHttpAuthManager * @@ -19,7 +21,7 @@ * Java client fetches content from a HTTP site that the user * has already logged into. */ -[scriptable, uuid(1301b517-ac72-48f6-a781-70c196eaaf3d)] +[scriptable, uuid(54f90444-c52b-4d2d-8916-c59a2bb25938)] interface nsIHttpAuthManager : nsISupports { /** @@ -45,6 +47,12 @@ interface nsIHttpAuthManager : nsISupports * return value containing user name. * @param aUserPassword * return value containing user password. + * @param aIsPrivate + * whether to look up a private or public identity (they are + * stored separately, for use by private browsing) + * @param aPrincipal + * the principal from which to derive information about which + * app/mozbrowser is in use for this request */ void getAuthIdentity(in ACString aScheme, in ACString aHost, @@ -55,7 +63,8 @@ interface nsIHttpAuthManager : nsISupports out AString aUserDomain, out AString aUserName, out AString aUserPassword, - [optional] in bool aIsPrivate); + [optional] in bool aIsPrivate, + [optional] in nsIPrincipal aPrincipal); /** * Store auth identity. @@ -80,6 +89,12 @@ interface nsIHttpAuthManager : nsISupports * optional string containing user name. * @param aUserPassword * optional string containing user password. + * @param aIsPrivate + * whether to store a private or public identity (they are + * stored separately, for use by private browsing) + * @param aPrincipal + * the principal from which to derive information about which + * app/mozbrowser is in use for this request */ void setAuthIdentity(in ACString aScheme, in ACString aHost, @@ -90,7 +105,8 @@ interface nsIHttpAuthManager : nsISupports in AString aUserDomain, in AString aUserName, in AString aUserPassword, - [optional] in boolean aIsPrivate); + [optional] in boolean aIsPrivate, + [optional] in nsIPrincipal aPrincipal); /** * Clear all auth cache. diff --git a/netwerk/test/unit/test_auth_jar.js b/netwerk/test/unit/test_auth_jar.js new file mode 100644 index 000000000000..c91251ad4902 --- /dev/null +++ b/netwerk/test/unit/test_auth_jar.js @@ -0,0 +1,52 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +function createURI(s) { + let service = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + return service.newURI(s, null, null); +} + +function run_test() { + var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); + const kURI1 = "http://example.com"; + var app1 = secMan.getAppCodebasePrincipal(createURI(kURI1), 1, false); + var app10 = secMan.getAppCodebasePrincipal(createURI(kURI1), 10, false); + var app1browser = secMan.getAppCodebasePrincipal(createURI(kURI1), 1, true); + + var am = Cc["@mozilla.org/network/http-auth-manager;1"]. + getService(Ci.nsIHttpAuthManager); + am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user", "pass", false, app1); + am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user3", "pass3", false, app1browser); + am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user2", "pass2", false, app10); + + let subject = { + appId: 1, + browserOnly: true, + QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams]) + }; + Services.obs.notifyObservers(subject, "webapps-clear-data", null); + + var domain = {value: ""}, user = {value: ""}, pass = {value: ""}; + try { + am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1browser); + do_check_false(true); // no identity should be present + } catch (x) { + do_check_eq(domain.value, ""); + do_check_eq(user.value, ""); + do_check_eq(pass.value, ""); + } + + am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1); + do_check_eq(domain.value, "example.com"); + do_check_eq(user.value, "user"); + do_check_eq(pass.value, "pass"); + + + am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app10); + do_check_eq(domain.value, "example.com"); + do_check_eq(user.value, "user2"); + do_check_eq(pass.value, "pass2"); +} \ No newline at end of file diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index e3957b880ee5..a467b182b1fe 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -9,6 +9,7 @@ tail = [test_URIs.js] [test_aboutblank.js] [test_assoc.js] +[test_auth_jar.js] [test_auth_proxy.js] [test_authentication.js] # Bug 675039: test hangs consistently on Android From f1ed1782426b01f0ff7fc8b83c9d9695b755867e Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 21 Dec 2012 11:32:52 -0500 Subject: [PATCH 107/217] Bug 785597 - Remove a bunch of unused code. r=snorp --- mobile/android/base/gfx/GLController.java | 52 +++++------------------ 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index 0498e8def9c6..83d86f9a5241 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -6,7 +6,6 @@ package org.mozilla.gecko.gfx; import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGL11; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; @@ -24,7 +23,6 @@ public class GLController { private EGL10 mEGL; private EGLDisplay mEGLDisplay; private EGLConfig mEGLConfig; - private EGLSurface mEGLSurface; private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4; @@ -37,42 +35,19 @@ public class GLController { EGL10.EGL_NONE }; - public GLController(LayerView view) { + GLController(LayerView view) { mView = view; mGLVersion = 2; mSurfaceValid = false; } - public void setGLVersion(int version) { + /* This function is invoked by JNI */ + void setGLVersion(int version) { mGLVersion = version; } - public EGLDisplay getEGLDisplay() { return mEGLDisplay; } - public EGLConfig getEGLConfig() { return mEGLConfig; } - public EGLSurface getEGLSurface() { return mEGLSurface; } - public LayerView getView() { return mView; } - - public boolean hasSurface() { - return mEGLSurface != null; - } - - public boolean swapBuffers() { - return mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface); - } - - public boolean checkForLostContext() { - if (mEGL.eglGetError() != EGL11.EGL_CONTEXT_LOST) { - return false; - } - - mEGLDisplay = null; - mEGLConfig = null; - mEGLSurface = null; - return true; - } - - // This function is invoked by JNI - public void resumeCompositorIfValid() { + /* This function is invoked by JNI */ + void resumeCompositorIfValid() { synchronized (this) { if (!mSurfaceValid) { return; @@ -81,9 +56,10 @@ public class GLController { mView.getListener().compositionResumeRequested(mWidth, mHeight); } - // Wait until we are allowed to use EGL functions on the Surface backing - // this window. This function is invoked by JNI - public synchronized void waitForValidSurface() { + /* Wait until we are allowed to use EGL functions on the Surface backing + * this window. + * This function is invoked by JNI */ + synchronized void waitForValidSurface() { while (!mSurfaceValid) { try { wait(); @@ -93,14 +69,6 @@ public class GLController { } } - public synchronized int getWidth() { - return mWidth; - } - - public synchronized int getHeight() { - return mHeight; - } - synchronized void surfaceDestroyed() { mSurfaceValid = false; notifyAll(); @@ -164,7 +132,7 @@ public class GLController { * Provides an EGLSurface without assuming ownership of this surface. * This class does not keep a reference to the provided EGL surface; the * caller assumes ownership of the surface once it is returned. - */ + * This function is invoked by JNI */ private EGLSurface provideEGLSurface() { if (mEGL == null) { initEGL(); From df745970bc639d96aee0099ac334828cd68ac6d4 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 21 Dec 2012 11:33:09 -0500 Subject: [PATCH 108/217] Bug 785597 - Get rid of unused mGLVersion field. r=snorp --- mobile/android/base/gfx/GLController.java | 7 ------- widget/android/AndroidBridge.cpp | 1 - widget/android/AndroidLayerViewWrapper.cpp | 10 ---------- widget/android/AndroidLayerViewWrapper.h | 2 -- 4 files changed, 20 deletions(-) diff --git a/mobile/android/base/gfx/GLController.java b/mobile/android/base/gfx/GLController.java index 83d86f9a5241..86c191903d24 100644 --- a/mobile/android/base/gfx/GLController.java +++ b/mobile/android/base/gfx/GLController.java @@ -16,7 +16,6 @@ public class GLController { private static final String LOGTAG = "GeckoGLController"; private LayerView mView; - private int mGLVersion; private boolean mSurfaceValid; private int mWidth, mHeight; @@ -37,15 +36,9 @@ public class GLController { GLController(LayerView view) { mView = view; - mGLVersion = 2; mSurfaceValid = false; } - /* This function is invoked by JNI */ - void setGLVersion(int version) { - mGLVersion = version; - } - /* This function is invoked by JNI */ void resumeCompositorIfValid() { synchronized (this) { diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 4c1f90468ed6..e27fc36ed0ef 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1198,7 +1198,6 @@ AndroidBridge::RegisterCompositor(JNIEnv *env, bool resetting) sController.Reacquire(env, glController); } else { sController.Acquire(env, glController); - sController.SetGLVersion(2); } } diff --git a/widget/android/AndroidLayerViewWrapper.cpp b/widget/android/AndroidLayerViewWrapper.cpp index fb499f5ca6e2..08157619c15c 100644 --- a/widget/android/AndroidLayerViewWrapper.cpp +++ b/widget/android/AndroidLayerViewWrapper.cpp @@ -21,7 +21,6 @@ void AndroidEGLObject::Init(JNIEnv* aJEnv) { jEGLSurfacePointerField = aJEnv->GetFieldID(jClass, "mEGLSurface", "I"); } -jmethodID AndroidGLController::jSetGLVersionMethod = 0; jmethodID AndroidGLController::jWaitForValidSurfaceMethod = 0; jmethodID AndroidGLController::jProvideEGLSurfaceMethod = 0; jmethodID AndroidGLController::jResumeCompositorIfValidMethod = 0; @@ -31,7 +30,6 @@ AndroidGLController::Init(JNIEnv* aJEnv) { jclass jClass = reinterpret_cast(aJEnv->NewGlobalRef(aJEnv->FindClass("org/mozilla/gecko/gfx/GLController"))); - jSetGLVersionMethod = aJEnv->GetMethodID(jClass, "setGLVersion", "(I)V"); jProvideEGLSurfaceMethod = aJEnv->GetMethodID(jClass, "provideEGLSurface", "()Ljavax/microedition/khronos/egl/EGLSurface;"); jResumeCompositorIfValidMethod = aJEnv->GetMethodID(jClass, "resumeCompositorIfValid", "()V"); @@ -46,14 +44,6 @@ AndroidGLController::Acquire(JNIEnv* aJEnv, jobject aJObj) mJObj = aJEnv->NewGlobalRef(aJObj); } -void -AndroidGLController::SetGLVersion(int aVersion) -{ - ASSERT_THREAD(); - AutoLocalJNIFrame jniFrame(mJEnv, 0); - mJEnv->CallVoidMethod(mJObj, jSetGLVersionMethod, aVersion); -} - void AndroidGLController::Reacquire(JNIEnv *aJEnv, jobject aJObj) { diff --git a/widget/android/AndroidLayerViewWrapper.h b/widget/android/AndroidLayerViewWrapper.h index ff94b4f91132..1b8b2111189d 100644 --- a/widget/android/AndroidLayerViewWrapper.h +++ b/widget/android/AndroidLayerViewWrapper.h @@ -21,13 +21,11 @@ public: static void Init(JNIEnv* aJEnv); void Acquire(JNIEnv* aJEnv, jobject aJObj); - void SetGLVersion(int aVersion); void Reacquire(JNIEnv* aJEnv, jobject aJObj); EGLSurface ProvideEGLSurface(); void WaitForValidSurface(); private: - static jmethodID jSetGLVersionMethod; static jmethodID jWaitForValidSurfaceMethod; static jmethodID jProvideEGLSurfaceMethod; static jmethodID jResumeCompositorIfValidMethod; From dc90835f0454e99154d36f2b86f298d47736c63a Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 21 Dec 2012 11:33:28 -0500 Subject: [PATCH 109/217] Bug 785597 - Don't create a LayerManager for a content window of an invisible top-level window. r=snorp --- widget/android/nsWindow.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index ea81ba7ecb9b..6c86c30ccdab 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -229,6 +229,10 @@ nsWindow::Create(nsIWidget *aParent, mParent = parent; } +#ifdef DEBUG_ANDROID_WIDGET + DumpWindows(); +#endif + return NS_OK; } @@ -250,6 +254,10 @@ nsWindow::Destroy(void) nsBaseWidget::OnDestroy(); +#ifdef DEBUG_ANDROID_WIDGET + DumpWindows(); +#endif + return NS_OK; } @@ -677,12 +685,10 @@ nsWindow::GetLayerManager(PLayersChild*, LayersBackend, LayerManagerPersistence, return mLayerManager; } - nsWindow *topWindow = TopWindow(); - - if (!topWindow) { - printf_stderr(" -- no topwindow\n"); - mLayerManager = CreateBasicLayerManager(); - return mLayerManager; + nsWindow *topLevelWindow = FindTopLevel(); + if (!topLevelWindow || topLevelWindow->mWindowType == eWindowType_invisible) { + // don't create a layer manager for an invisible top-level window + return nullptr; } mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration); From 610ef445f968c80d5a2a8162bc6867bdb98b16eb Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Fri, 21 Dec 2012 18:50:26 +0100 Subject: [PATCH 110/217] Bug 725587 - Firefox jumps randomly from IPv6 to IPv4 and vice versa in dual-stack environment, r=mcmanus --- netwerk/base/public/nsISocketTransport.idl | 6 +++ netwerk/base/src/nsSocketTransport2.cpp | 19 ++++--- netwerk/dns/nsDNSService2.cpp | 3 ++ netwerk/dns/nsIDNSService.idl | 5 ++ netwerk/protocol/http/nsHttpChannel.cpp | 4 +- netwerk/protocol/http/nsHttpConnectionMgr.cpp | 52 +++++++++++++++++-- netwerk/protocol/http/nsHttpConnectionMgr.h | 17 ++++++ 7 files changed, 95 insertions(+), 11 deletions(-) diff --git a/netwerk/base/public/nsISocketTransport.idl b/netwerk/base/public/nsISocketTransport.idl index 2a848a352d97..9c0aa3ee6f40 100644 --- a/netwerk/base/public/nsISocketTransport.idl +++ b/netwerk/base/public/nsISocketTransport.idl @@ -160,6 +160,12 @@ interface nsISocketTransport : nsITransport */ const unsigned long NO_PERMANENT_STORAGE = (1 << 3); + /** + * If set, we will skip all IPv4 addresses the host may have and only + * connect to IPv6 ones. + */ + const unsigned long DISABLE_IPV4 = (1 << 4); + /** * Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or * IPTOS_CLASS_CSx (or IPTOS_DSCP_EF, but currently no supported diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index dd7eafc1f41a..d458ba1efac2 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -922,6 +922,12 @@ nsSocketTransport::ResolveHost() dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE; if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6) dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6; + if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4) + dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4; + + NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) || + !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4), + "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4"); SendStatus(NS_NET_STATUS_RESOLVING_HOST); rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nullptr, @@ -1261,12 +1267,12 @@ nsSocketTransport::RecoverFromError() bool tryAgain = false; - if (mConnectionFlags & DISABLE_IPV6 && + if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4) && mCondition == NS_ERROR_UNKNOWN_HOST && mState == STATE_RESOLVING && !mProxyTransparentResolvesHost) { SOCKET_LOG((" trying lookup again with both ipv4/ipv6 enabled\n")); - mConnectionFlags &= ~DISABLE_IPV6; + mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4); tryAgain = true; } @@ -1279,14 +1285,15 @@ nsSocketTransport::RecoverFromError() SOCKET_LOG((" trying again with next ip address\n")); tryAgain = true; } - else if (mConnectionFlags & DISABLE_IPV6) { + else if (mConnectionFlags & (DISABLE_IPV6 | DISABLE_IPV4)) { // Drop state to closed. This will trigger new round of DNS // resolving bellow. - // XXX Here should idealy be set now non-existing flag DISABLE_IPV4 - SOCKET_LOG((" failed to connect all ipv4 hosts," + // XXX Could be optimized to only switch the flags to save duplicate + // connection attempts. + SOCKET_LOG((" failed to connect all ipv4-only or ipv6-only hosts," " trying lookup/connect again with both ipv4/ipv6\n")); mState = STATE_CLOSED; - mConnectionFlags &= ~DISABLE_IPV6; + mConnectionFlags &= ~(DISABLE_IPV6 | DISABLE_IPV4); tryAgain = true; } } diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 7af99e987b40..84d01eb02a30 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -851,6 +851,9 @@ nsDNSService::GetAFForLookup(const nsACString &host, uint32_t flags) } while (*end); } + if ((af != PR_AF_INET) && (flags & RESOLVE_DISABLE_IPV4)) + af = PR_AF_INET6; + return af; } diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index c3ebd2ee23e1..11e265f858e3 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -133,4 +133,9 @@ interface nsIDNSService : nsISupports * asyncResolve. */ const unsigned long RESOLVE_OFFLINE = (1 << 6); + + /** + * If set, only IPv6 addresses will be returned from resolve/asyncResolve. + */ + const unsigned long RESOLVE_DISABLE_IPV4 = (1 << 7); }; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 49d5d6446a79..3e71c8fee4bc 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -4455,8 +4455,10 @@ nsHttpChannel::BeginConnect() // Force-Reload should reset the persistent connection pool for this host if (mLoadFlags & LOAD_FRESH_CONNECTION) { // just the initial document resets the whole pool - if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) + if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { gHttpHandler->ConnMgr()->ClosePersistentConnections(); + gHttpHandler->ConnMgr()->ResetIPFamillyPreference(mConnectionInfo); + } // each sub resource gets a fresh connection mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING); } diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index db0d609ddeb6..c58e81eb6c7b 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -2519,8 +2519,13 @@ nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport, // IPv6 on the backup connection gives them a much better user experience // with dual-stack hosts, though they still pay the 250ms delay for each new // connection. This strategy is also known as "happy eyeballs". - if (isBackup && gHttpHandler->FastFallbackToIPv4()) + if (mEnt->mPreferIPv6) { + tmpFlags |= nsISocketTransport::DISABLE_IPV4; + } + else if (mEnt->mPreferIPv4 || + (isBackup && gHttpHandler->FastFallbackToIPv4())) { tmpFlags |= nsISocketTransport::DISABLE_IPV6; + } socketTransport->SetConnectionFlags(tmpFlags); @@ -2712,6 +2717,7 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out) LOG(("nsHalfOpenSocket::OnOutputStreamReady " "Created new nshttpconnection %p\n", conn.get())); + PRNetAddr peeraddr; nsCOMPtr callbacks; mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); if (out == mStreamOut) { @@ -2722,6 +2728,9 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out) callbacks, PR_MillisecondsToInterval(rtt.ToMilliseconds())); + if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) + mEnt->RecordIPFamilyPreference(peeraddr.raw.family); + // The nsHttpConnection object now owns these streams and sockets mStreamOut = nullptr; mStreamIn = nullptr; @@ -2735,6 +2744,9 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out) callbacks, PR_MillisecondsToInterval(rtt.ToMilliseconds())); + if (NS_SUCCEEDED(mBackupTransport->GetPeerAddr(&peeraddr))) + mEnt->RecordIPFamilyPreference(peeraddr.raw.family); + // The nsHttpConnection object now owns these streams and sockets mBackupStreamOut = nullptr; mBackupStreamIn = nullptr; @@ -2947,6 +2959,8 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci) , mUsingSpdy(false) , mTestedSpdy(false) , mSpdyPreferred(false) + , mPreferIPv4(false) + , mPreferIPv6(false) { NS_ADDREF(mConnInfo); if (gHttpHandler->GetPipelineAggressive()) { @@ -3102,7 +3116,8 @@ nsConnectionEntry::SetYellowConnection(nsHttpConnection *conn) } void -nsHttpConnectionMgr::nsConnectionEntry::OnYellowComplete() +nsHttpConnectionMgr:: +nsConnectionEntry::OnYellowComplete() { if (mPipelineState == PS_YELLOW) { if (mYellowGoodEvents && !mYellowBadEvents) { @@ -3125,7 +3140,8 @@ nsHttpConnectionMgr::nsConnectionEntry::OnYellowComplete() } void -nsHttpConnectionMgr::nsConnectionEntry::CreditPenalty() +nsHttpConnectionMgr:: +nsConnectionEntry::CreditPenalty() { if (mLastCreditTime.IsNull()) return; @@ -3221,8 +3237,17 @@ nsHttpConnectionMgr::GetConnectionData(nsTArray *aA return true; } +void +nsHttpConnectionMgr::ResetIPFamillyPreference(nsHttpConnectionInfo *ci) +{ + nsConnectionEntry *ent = LookupConnectionEntry(ci, nullptr, nullptr); + if (ent) + ent->ResetIPFamilyPreference(); +} + uint32_t -nsHttpConnectionMgr::nsConnectionEntry::UnconnectedHalfOpens() +nsHttpConnectionMgr:: +nsConnectionEntry::UnconnectedHalfOpens() { uint32_t unconnectedHalfOpens = 0; for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) { @@ -3248,3 +3273,22 @@ nsConnectionEntry::RemoveHalfOpen(nsHalfOpenSocket *halfOpen) // altering the pending q vector from an arbitrary stack gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo); } + +void +nsHttpConnectionMgr:: +nsConnectionEntry::RecordIPFamilyPreference(uint16_t family) +{ + if (family == PR_AF_INET && !mPreferIPv6) + mPreferIPv4 = true; + + if (family == PR_AF_INET6 && !mPreferIPv4) + mPreferIPv6 = true; +} + +void +nsHttpConnectionMgr:: +nsConnectionEntry::ResetIPFamilyPreference() +{ + mPreferIPv4 = false; + mPreferIPv6 = false; +} diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index a27b9598959e..30d917bc6eae 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -225,6 +225,9 @@ public: bool SupportsPipelining(nsHttpConnectionInfo *); bool GetConnectionData(nsTArray *); + + void ResetIPFamillyPreference(nsHttpConnectionInfo *); + private: virtual ~nsHttpConnectionMgr(); @@ -343,6 +346,20 @@ private: bool mTestedSpdy; bool mSpdyPreferred; + + // Flags to remember our happy-eyeballs decision. + // Reset only by Ctrl-F5 reload. + // True when we've first connected an IPv4 server for this host, + // initially false. + bool mPreferIPv4 : 1; + // True when we've first connected an IPv6 server for this host, + // initially false. + bool mPreferIPv6 : 1; + + // Set the IP family preference flags according the connected family + void RecordIPFamilyPreference(uint16_t family); + // Resets all flags to their default values + void ResetIPFamilyPreference(); }; // nsConnectionHandle From 2177e96cef44abc703d62394bd9bf9a10879362f Mon Sep 17 00:00:00 2001 From: Hannes Verschore Date: Fri, 21 Dec 2012 18:53:19 +0100 Subject: [PATCH 111/217] Bug 813784: Allow inlining of |arguments| in IM, r=nbp --- js/src/ion/IonBuilder.cpp | 183 ++++++++++++++++++------ js/src/ion/IonBuilder.h | 15 +- js/src/ion/IonFrameIterator-inl.h | 31 +++-- js/src/ion/IonFrameIterator.h | 1 + js/src/ion/IonFrames.cpp | 15 ++ js/src/ion/TypeOracle.cpp | 2 +- js/src/jit-test/tests/ion/bug813784.js | 185 +++++++++++++++++++++++++ js/src/jsanalyze.cpp | 45 +++--- js/src/jsanalyze.h | 9 +- js/src/jsfun.cpp | 7 +- js/src/methodjit/Compiler.cpp | 2 +- js/src/vm/ArgumentsObject.cpp | 2 +- 12 files changed, 420 insertions(+), 77 deletions(-) create mode 100644 js/src/jit-test/tests/ion/bug813784.js diff --git a/js/src/ion/IonBuilder.cpp b/js/src/ion/IonBuilder.cpp index 52d437cda011..cb39556a358e 100644 --- a/js/src/ion/IonBuilder.cpp +++ b/js/src/ion/IonBuilder.cpp @@ -441,10 +441,14 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi if (!current->addPredecessorWithoutPhis(predecessor)) return false; + // Save the actual arguments the caller used to call this inlined call, + // to shortcut operations on "arguments" in the inlined call. + // Discard first argument, because it is |this| + inlinedArguments_.append(argv.begin() + 1, argv.end()); + // Explicitly pass Undefined for missing arguments. const size_t numActualArgs = argv.length() - 1; const size_t nargs = info().nargs(); - if (numActualArgs < nargs) { const size_t missing = nargs - numActualArgs; @@ -490,6 +494,11 @@ IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoi // +2 for the scope chain and |this|. JS_ASSERT(current->entryResumePoint()->numOperands() == nargs + info().nlocals() + 2); + if (script_->argumentsHasVarBinding()) { + lazyArguments_ = MConstant::New(MagicValue(JS_OPTIMIZED_ARGUMENTS)); + current->add(lazyArguments_); + } + return traverseBytecode(); } @@ -883,6 +892,7 @@ IonBuilder::inspectOpcode(JSOp op) return true; case JSOP_SETARG: + JS_ASSERT(inliningDepth == 0); // To handle this case, we should spill the arguments to the space where // actual arguments are stored. The tricky part is that if we add a MIR // to wrap the spilling action, we don't want the spilling to be @@ -3233,11 +3243,6 @@ IonBuilder::makeInliningDecision(AutoObjectVector &targets, uint32_t argc) targetScript = target->nonLazyScript(); uint32_t calleeUses = targetScript->getUseCount(); - if (target->nargs < argc) { - IonSpew(IonSpew_Inlining, "Not inlining, overflow of arguments."); - return false; - } - totalSize += targetScript->length; if (totalSize > js_IonOptions.inlineMaxTotalBytecodeLength) return false; @@ -3529,7 +3534,7 @@ IonBuilder::inlineScriptedCall(AutoObjectVector &targets, uint32_t argc, bool co MGetPropertyCache *getPropCache = NULL; if (!constructing) { getPropCache = checkInlineableGetPropertyCache(argc); - if(getPropCache) { + if (getPropCache) { InlinePropertyTable *inlinePropTable = getPropCache->inlinePropertyTable(); // checkInlineableGetPropertyCache should have verified this. JS_ASSERT(inlinePropTable != NULL); @@ -4027,6 +4032,13 @@ IonBuilder::jsop_funapply(uint32_t argc) return abort("fun.apply speculation failed"); } + // Use funapply that definitely uses |arguments| + return jsop_funapplyarguments(argc); +} + +bool +IonBuilder::jsop_funapplyarguments(uint32_t argc) +{ // Stack for JSOP_FUNAPPLY: // 1: MPassArg(Vp) // 2: MPassArg(This) @@ -4043,11 +4055,53 @@ IonBuilder::jsop_funapply(uint32_t argc) passVp->replaceAllUsesWith(passVp->getArgument()); passVp->block()->discard(passVp); + // When this script isn't inlined, use MApplyArgs, + // to copy the arguments from the stack and call the function + if (inliningDepth == 0) { + // This + MPassArg *passThis = current->pop()->toPassArg(); + MDefinition *argThis = passThis->getArgument(); + passThis->replaceAllUsesWith(argThis); + passThis->block()->discard(passThis); + + // Unwrap the (JSFunction *) parameter. + MPassArg *passFunc = current->pop()->toPassArg(); + MDefinition *argFunc = passFunc->getArgument(); + passFunc->replaceAllUsesWith(argFunc); + passFunc->block()->discard(passFunc); + + // Pop apply function. + current->pop(); + + MArgumentsLength *numArgs = MArgumentsLength::New(); + current->add(numArgs); + + MApplyArgs *apply = MApplyArgs::New(target, argFunc, numArgs, argThis); + current->add(apply); + current->push(apply); + if (!resumeAfter(apply)) + return false; + + types::StackTypeSet *barrier; + types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier); + return pushTypeBarrier(apply, types, barrier); + } + + // When inlining we have the arguments the function gets called with + // and can optimize even more, by just calling the functions with the args. + JS_ASSERT(inliningDepth > 0); + + // Arguments + Vector args(cx); + args.reserve(inlinedArguments_.length()); + for (size_t i = 0; i < inlinedArguments_.length(); i++) { + MPassArg *pass = MPassArg::New(inlinedArguments_[i]); + current->add(pass); + args.append(pass); + } + // This - MPassArg *passThis = current->pop()->toPassArg(); - MDefinition *argThis = passThis->getArgument(); - passThis->replaceAllUsesWith(argThis); - passThis->block()->discard(passThis); + MPassArg *thisArg = current->pop()->toPassArg(); // Unwrap the (JSFunction *) parameter. MPassArg *passFunc = current->pop()->toPassArg(); @@ -4058,18 +4112,7 @@ IonBuilder::jsop_funapply(uint32_t argc) // Pop apply function. current->pop(); - MArgumentsLength *numArgs = MArgumentsLength::New(); - current->add(numArgs); - - MApplyArgs *apply = MApplyArgs::New(target, argFunc, numArgs, argThis); - current->add(apply); - current->push(apply); - if (!resumeAfter(apply)) - return false; - - types::StackTypeSet *barrier; - types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier); - return pushTypeBarrier(apply, types, barrier); + return makeCall(target, false, argFunc, thisArg, args); } bool @@ -4206,12 +4249,42 @@ FreezeDOMTypes(JSContext *cx, types::StackTypeSet *inTypes) } } +void +IonBuilder::popFormals(uint32_t argc, MDefinition **fun, MPassArg **thisArg, + Vector *args) +{ + // Get the arguments in the right order + (*args).reserve(argc); + for (int32_t i = argc; i > 0; i--) + (*args).append(current->peek(-i)->toPassArg()); + + // Pop all arguments + for (int32_t i = argc; i > 0; i--) + current->pop(); + + *thisArg = current->pop()->toPassArg(); + *fun = current->pop(); +} + MCall * IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructing) +{ + Vector args(cx); + MPassArg *thisArg; + MDefinition *fun; + + popFormals(argc, &fun, &thisArg, &args); + return makeCallHelper(target, constructing, fun, thisArg, args); +} + +MCall * +IonBuilder::makeCallHelper(HandleFunction target, bool constructing, + MDefinition *fun, MPassArg *thisArg, Vector &args) { // This function may be called with mutated stack. // Querying TI for popped types is invalid. + uint32_t argc = args.length(); uint32_t targetArgs = argc; // Collect number of missing arguments provided that the target is @@ -4235,23 +4308,19 @@ IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructi } // Add explicit arguments. - // Bytecode order: Function, This, Arg0, Arg1, ..., ArgN, Call. - for (int32_t i = argc; i > 0; i--) - call->addArg(i, current->pop()->toPassArg()); + // Skip addArg(0) because it is reserved for this + for (int32_t i = argc - 1; i >= 0; i--) + call->addArg(i + 1, args[i]); // Place an MPrepareCall before the first passed argument, before we // potentially perform rearrangement. MPrepareCall *start = new MPrepareCall; - MPassArg *firstArg = current->peek(-1)->toPassArg(); - firstArg->block()->insertBefore(firstArg, start); + thisArg->block()->insertBefore(thisArg, start); call->initPrepareCall(start); - MPassArg *thisArg = current->pop()->toPassArg(); - // Inline the constructor on the caller-side. if (constructing) { - MDefinition *callee = current->peek(-1); - MDefinition *create = createThis(target, callee); + MDefinition *create = createThis(target, fun); if (!create) { abort("Failure inlining constructor for call."); return NULL; @@ -4280,7 +4349,6 @@ IonBuilder::makeCallHelper(HandleFunction target, uint32_t argc, bool constructi call->setDOMFunction(); } } - MDefinition *fun = current->pop(); call->initFunction(fun); current->add(call); @@ -4293,7 +4361,22 @@ IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc, types::StackTypeSet *types, types::StackTypeSet *barrier) { - MCall *call = makeCallHelper(target, argc, constructing); + Vector args(cx); + MPassArg *thisArg; + MDefinition *fun; + + popFormals(argc, &fun, &thisArg, &args); + return makeCallBarrier(target, constructing, fun, thisArg, args, types, barrier); +} + +bool +IonBuilder::makeCallBarrier(HandleFunction target, bool constructing, + MDefinition *fun, MPassArg *thisArg, + Vector &args, + types::StackTypeSet *types, + types::StackTypeSet *barrier) +{ + MCall *call = makeCallHelper(target, constructing, fun, thisArg, args); if (!call) return false; @@ -4306,10 +4389,23 @@ IonBuilder::makeCallBarrier(HandleFunction target, uint32_t argc, bool IonBuilder::makeCall(HandleFunction target, uint32_t argc, bool constructing) +{ + Vector args(cx); + MPassArg *thisArg; + MDefinition *fun; + + popFormals(argc, &fun, &thisArg, &args); + return makeCall(target, constructing, fun, thisArg, args); +} + +bool +IonBuilder::makeCall(HandleFunction target, bool constructing, + MDefinition *fun, MPassArg *thisArg, + Vector &args) { types::StackTypeSet *barrier; types::StackTypeSet *types = oracle->returnTypeSet(script(), pc, &barrier); - return makeCallBarrier(target, argc, constructing, types, barrier); + return makeCallBarrier(target, constructing, fun, thisArg, args, types, barrier); } bool @@ -5770,15 +5866,24 @@ IonBuilder::jsop_arguments_length() MDefinition *args = current->pop(); args->setFoldedUnchecked(); - MInstruction *ins = MArgumentsLength::New(); - current->add(ins); - current->push(ins); - return true; + // We don't know anything from the callee + if (inliningDepth == 0) { + MInstruction *ins = MArgumentsLength::New(); + current->add(ins); + current->push(ins); + return true; + } + + // We are inlining and know the number of arguments the callee pushed + return pushConstant(Int32Value(inlinedArguments_.length())); } bool IonBuilder::jsop_arguments_getelem() { + if (inliningDepth != 0) + return abort("NYI inlined get argument element"); + RootedScript scriptRoot(cx, script()); types::StackTypeSet *barrier = oracle->propertyReadBarrier(scriptRoot, pc); types::StackTypeSet *types = oracle->propertyRead(script(), pc); diff --git a/js/src/ion/IonBuilder.h b/js/src/ion/IonBuilder.h index 3221e838c370..e998b3927e06 100644 --- a/js/src/ion/IonBuilder.h +++ b/js/src/ion/IonBuilder.h @@ -299,8 +299,6 @@ class IonBuilder : public MIRGenerator MInstruction *createDeclEnvObject(MDefinition *callee, MDefinition *scopeObj); MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj); - bool makeCall(HandleFunction target, uint32_t argc, bool constructing); - MDefinition *walkScopeChain(unsigned hops); MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length); @@ -339,6 +337,7 @@ class IonBuilder : public MIRGenerator bool jsop_notearg(); bool jsop_funcall(uint32_t argc); bool jsop_funapply(uint32_t argc); + bool jsop_funapplyarguments(uint32_t argc); bool jsop_call(uint32_t argc, bool constructing); bool jsop_ifeq(JSOp op); bool jsop_condswitch(); @@ -440,9 +439,19 @@ class IonBuilder : public MIRGenerator types::StackTypeSet *types, types::StackTypeSet *barrier); bool makeInliningDecision(AutoObjectVector &targets, uint32_t argc); + void popFormals(uint32_t argc, MDefinition **fun, MPassArg **thisArg, + Vector *args); + MCall *makeCallHelper(HandleFunction target, bool constructing, + MDefinition *fun, MPassArg *thisArg, Vector &args); MCall *makeCallHelper(HandleFunction target, uint32_t argc, bool constructing); bool makeCallBarrier(HandleFunction target, uint32_t argc, bool constructing, types::StackTypeSet *types, types::StackTypeSet *barrier); + bool makeCallBarrier(HandleFunction target, bool constructing, + MDefinition *fun, MPassArg *thisArg, Vector &args, + types::StackTypeSet *types, types::StackTypeSet *barrier); + bool makeCall(HandleFunction target, uint32_t argc, bool constructing); + bool makeCall(HandleFunction target, bool constructing, + MDefinition *fun, MPassArg *thisArg, Vector &args); inline bool TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types, HandleId id, JSFunction **funcp, @@ -501,7 +510,9 @@ class IonBuilder : public MIRGenerator Vector switches_; Vector iterators_; TypeOracle *oracle; + size_t inliningDepth; + Vector inlinedArguments_; // True if script->failedBoundsCheck is set for the current script or // an outer script. diff --git a/js/src/ion/IonFrameIterator-inl.h b/js/src/ion/IonFrameIterator-inl.h index 75d78ee91dba..59f859c0c6bc 100644 --- a/js/src/ion/IonFrameIterator-inl.h +++ b/js/src/ion/IonFrameIterator-inl.h @@ -60,15 +60,30 @@ InlineFrameIterator::forEachCanonicalActualArg(Op op, unsigned start, unsigned c JS_ASSERT(start <= end && end <= nactual); - // Currently inlining does not support overflow of arguments, we have to - // add this feature in IonBuilder.cpp and in Bailouts.cpp before - // continuing. We need to add it to Bailouts.cpp because we need to know - // how to walk over the oveflow of arguments. - JS_ASSERT_IF(more(), end <= nformal); + if (more()) { + // There is still a parent frame of this inlined frame. + // Take arguments of the caller (parent inlined frame) it holds all actual + // arguments, needed in case of overflow, and because the analyze phase + // disable Ion inlining if the function redefine its arguments with JSOP_SETARG. + + InlineFrameIterator it(this); + ++it; + SnapshotIterator s(it.snapshotIterator()); + + // Skip over all slots untill we get to the arguments slots + // the +2 is for [this] and [scopechain] + JS_ASSERT(s.slots() >= nactual + 2); + unsigned skip = s.slots() - nactual - 2; + for (unsigned j = 0; j < skip; j++) + s.skip(); + + s.readFrameArgs(op, NULL, NULL, NULL, start, nactual, end); + } else { + SnapshotIterator s(si_); + Value *argv = frame_->actualArgs(); + s.readFrameArgs(op, argv, NULL, NULL, start, nformal, end); + } - SnapshotIterator s(si_); - Value *argv = frame_->actualArgs(); - s.readFrameArgs(op, argv, NULL, NULL, start, nformal, end); } } // namespace ion diff --git a/js/src/ion/IonFrameIterator.h b/js/src/ion/IonFrameIterator.h index 76cf6c83f101..5b2a32a97280 100644 --- a/js/src/ion/IonFrameIterator.h +++ b/js/src/ion/IonFrameIterator.h @@ -275,6 +275,7 @@ class InlineFrameIterator public: InlineFrameIterator(const IonFrameIterator *iter); InlineFrameIterator(const IonBailoutIterator *iter); + InlineFrameIterator(const InlineFrameIterator *iter); bool more() const { return frame_ && framesRead_ < start_.frameCount(); diff --git a/js/src/ion/IonFrames.cpp b/js/src/ion/IonFrames.cpp index 88d52b1561e2..5ae692f392c0 100644 --- a/js/src/ion/IonFrames.cpp +++ b/js/src/ion/IonFrames.cpp @@ -887,6 +887,21 @@ InlineFrameIterator::InlineFrameIterator(const IonFrameIterator *iter) } } +InlineFrameIterator::InlineFrameIterator(const InlineFrameIterator *iter) + : frame_(iter->frame_), + framesRead_(0), + callee_(NULL), + script_(NULL) +{ + if (frame_) { + start_ = SnapshotIterator(*frame_); + // findNextFrame will iterate to the next frame and init. everything. + // Therefore to settle on the same frame, we report one frame less readed. + framesRead_ = iter->framesRead_ - 1; + findNextFrame(); + } +} + void InlineFrameIterator::findNextFrame() { diff --git a/js/src/ion/TypeOracle.cpp b/js/src/ion/TypeOracle.cpp index 696a1160925b..fb8abdac4ad1 100644 --- a/js/src/ion/TypeOracle.cpp +++ b/js/src/ion/TypeOracle.cpp @@ -561,7 +561,7 @@ TypeInferenceOracle::canEnterInlinedFunction(JSFunction *target) if (!script->hasAnalysis() || !script->analysis()->ranInference()) return false; - if (!script->analysis()->inlineable()) + if (!script->analysis()->ionInlineable()) return false; if (script->analysis()->usesScopeChain()) diff --git a/js/src/jit-test/tests/ion/bug813784.js b/js/src/jit-test/tests/ion/bug813784.js new file mode 100644 index 000000000000..1a546d912ad0 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug813784.js @@ -0,0 +1,185 @@ +/* Test an inlined argument returns the arguments from the right function */ +function get_arg_2() { return arguments[2]; } +function test() { return get_arg_2(1,2,3); } + +assertEq(test("a","b","c"), 3); +assertEq(test("a","b","c"), 3); + +/* Test the right length of the argument vector gets returned */ +function arg_len() { return arguments.length; } +function test2() { return arg_len(1,2,3); } + +assertEq(test2("a","b","c","d"), 3); +assertEq(test2("a","b","c","d"), 3); + +/* Test returning the argument vector */ +function get_arg() { return arguments; } +function test3() { return get_arg(1,2,3) } + +var arg = test3("a","b","c","d","e"); +assertEq(arg.length, 3); +assertEq(arg[0], 1); +assertEq(arg[1], 2); +assertEq(arg[2], 3); +var arg = test3("a","b","c","d","e"); +assertEq(arg.length, 3); +assertEq(arg[0], 1); +assertEq(arg[1], 2); +assertEq(arg[2], 3); + +/* Test funapply with arguments */ +function return0(a, b, c) { return 0; } +function funapply() { return return0.apply({}, arguments); } +function test4() { return funapply(1,2,3) } + +assertEq(test4("a","b","c","d","e"), 0); +assertEq(test4("a","b","c","d","e"), 0); + +/* Test if funapply gets the right arguments */ +function apply3(a, b, c) { + assertEq(a,1) + assertEq(b,2) + assertEq(c,3) +} +function funapply2() { return apply3.apply({}, arguments); } +function test5() { return funapply2(1,2,3) } + +test5("a","b","c","d","e"); +test5("a","b","c","d","e"); + +/* Test funapply when argument vector has less args than callee and callee known */ +function apply_fun1(a, b, c) { assertEq(c, undefined) } +function funapply3() { return apply_fun1.apply({}, arguments); } +function test7() { return funapply3(1,2) } + +test7("a","b","c","d","e"); +test7("a","b","c","d","e"); + +/* Test funapply when argument vector has less args than callee and callee unknown */ +var fun; +function apply_fun2(a, b, c) { assertEq(c, undefined) } +function funapply4() { return fun.apply({}, arguments); } +function test8() { return funapply4(1,2) } + +fun = apply_fun1; +test8("a","b","c","d","e"); +fun = apply_fun2; +test8("a","b","c","d","e"); +fun = apply_fun1; +test8("a","b","c","d","e"); +fun = apply_fun2; +test8("a","b","c","d","e"); + +//////////// + +function dumpArgs(i) { if (i == 90) return funapply5.arguments.length; return [i]; } +function funapply5() { return dumpArgs.apply({}, arguments); } +function test9(i) { return funapply5(i); } + +assertEq(test9(89)[0], 89); +assertEq(test9(90), 1); + +///////////// + +function notinlined() { + assertEq(arguments[0], 4); + assertEq(arguments[1], 5); + assertEq(arguments[2], 6); +} + +function inline2(a) { return notinlined(4,5,6); } +function inline() { return inline2(1,2,3); } +function base1() { return inline(-1,-2,-3); } + +base1(10,11,12); +base1(10,11,12); + +//////////////// + +function inlined(a) { + if (a == 11) { + a = undefined; + return arguments; + } +} + +function inline4(a) { return inlined(a,5,6); } +function inline3(a) { return inline4(a,2,3); } +function base2(a) { return inline3(a,-2,-3); } + +var args = base2(10,11,12); +assertEq(args, undefined); +var args = base2(11,11,12); +assertEq(args[0], undefined); +assertEq(args[1], 5); +assertEq(args[2], 6); +var args = base2(10,11,12); +assertEq(args, undefined); +var args = base2(11,11,12); +assertEq(args[0], undefined); +assertEq(args[1], 5); +assertEq(args[2], 6); + +////////////////// + +var bailout = Proxy.createFunction({}, Math.sin); +function arg_len2() { assertEq(arguments.length, 4); } +function bailing_arg_len(a) { + if (a == 90) { + bailout(); + arg_len.apply({}, arguments); + } + assertEq(arguments.length, 4); + return arguments; +} +function test10(i) { return bailing_arg_len(i,2,3,4); } + +var args = test10(1, "b"); +assertEq(args.length, 4) +assertEq(args[0], 1) +assertEq(args[1], 2) +assertEq(args[2], 3) +assertEq(args[3], 4) + +var args = test10(90, 'b'); +assertEq(args.length, 4) +assertEq(args[0], 90) +assertEq(args[1], 2) +assertEq(args[2], 3) +assertEq(args[3], 4) + +//////////// + +function dumpArgs11(i) { return funapply11.arguments; eval(""); } +function funapply11(i) { return dumpArgs11(i); } +function test11(i) { return funapply11(i); } + +assertEq(test11(89)[0], 89); +assertEq(test11(90)[0], 90); + +//////////// + +function dumpArgs12(i) { if (i == 90) return funapply12.arguments; return [i]; } +function noion12(i) { return dumpArgs12(i); eval(""); } +function funapply12(i) { return noion12(i); } +function test12(i) { return funapply12(i); } + +assertEq(test12("89")[0], "89"); +assertEq(test12("90")[0], "90"); + +//////////// + +function f13(i) { if (i == "90") return f13.arguments; return [i]; } +function test13(i,b) { return f13(i,b); } + +assertEq(test13("89", 1)[0], "89"); +assertEq(test13("90", 2)[1], 2); + +/////////// + +function noion14(i) { if (i == 2) { return funapply14.arguments; } return [i]; eval(""); } +function funapply14(i) { if (i == 90) { i = "2"; } return noion14(i); } +function test14(i) { return funapply14(i); } + +assertEq(test14("89")[0], "89"); +assertEq(test14("90")[0], "2"); diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 9871b39585da..d6c2c7167a6c 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -75,7 +75,7 @@ ScriptAnalysis::addJump(JSContext *cx, unsigned offset, if (offset < *currentOffset) { /* Scripts containing loops are never inlined. */ - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; hasLoops_ = true; if (code->analyzed) { @@ -163,9 +163,11 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) isJaegerCompileable = true; - isInlineable = true; - if (heavyweight || script_->argumentsHasVarBinding() || cx->compartment->debugMode()) - isInlineable = false; + isJaegerInlineable = isIonInlineable = true; + if (heavyweight || cx->compartment->debugMode()) + isJaegerInlineable = isIonInlineable = false; + if (script_->argumentsHasVarBinding()) + isJaegerInlineable = false; modifiesArguments_ = false; if (heavyweight) @@ -257,7 +259,8 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) if (script_->hasBreakpointsAt(pc)) { code->safePoint = true; - isInlineable = canTrackVars = false; + canTrackVars = false; + isJaegerInlineable = isIonInlineable = false; } unsigned stackDepth = code->stackDepth; @@ -304,7 +307,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_SETRVAL: case JSOP_POPV: usesReturnValue_ = true; - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; break; case JSOP_QNAMEPART: @@ -321,7 +324,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_SETALIASEDVAR: case JSOP_LAMBDA: usesScopeChain_ = true; - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; break; case JSOP_DEFFUN: @@ -329,22 +332,25 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_DEFCONST: case JSOP_SETCONST: usesScopeChain_ = true; // Requires access to VarObj via ScopeChain. - isInlineable = canTrackVars = false; + canTrackVars = false; + isJaegerInlineable = isIonInlineable = false; break; case JSOP_EVAL: - isInlineable = canTrackVars = false; + canTrackVars = false; + isJaegerInlineable = isIonInlineable = false; break; case JSOP_ENTERWITH: - isJaegerCompileable = isInlineable = canTrackVars = false; + isJaegerCompileable = canTrackVars = false; + isJaegerInlineable = isIonInlineable = false; break; case JSOP_ENTERLET0: case JSOP_ENTERLET1: case JSOP_ENTERBLOCK: case JSOP_LEAVEBLOCK: - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; break; case JSOP_THIS: @@ -358,7 +364,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) break; case JSOP_TABLESWITCH: { - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; int32_t low = GET_JUMP_OFFSET(pc2); @@ -385,7 +391,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) } case JSOP_LOOKUPSWITCH: { - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; unsigned npairs = GET_UINT16(pc2); @@ -416,7 +422,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) * exception but is not caught by a later handler in the same function: * no more code will execute, and it does not matter what is defined. */ - isInlineable = false; + isJaegerInlineable = isIonInlineable = false; JSTryNote *tn = script_->trynotes()->vector; JSTryNote *tnlimit = tn + script_->trynotes()->length; for (; tn < tnlimit; tn++) { @@ -468,7 +474,7 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) case JSOP_SETARG: modifiesArguments_ = true; - isInlineable = false; + isIonInlineable = isJaegerInlineable = false; break; case JSOP_GETPROP: @@ -481,12 +487,14 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) /* Additional opcodes which can be compiled but which can't be inlined. */ case JSOP_ARGUMENTS: + case JSOP_FUNAPPLY: + isJaegerInlineable = false; + break; case JSOP_THROW: case JSOP_EXCEPTION: case JSOP_DEBUGGER: case JSOP_FUNCALL: - case JSOP_FUNAPPLY: - isInlineable = false; + isIonInlineable = isJaegerInlineable = false; break; /* Additional opcodes which can be both compiled both normally and inline. */ @@ -586,7 +594,8 @@ ScriptAnalysis::analyzeBytecode(JSContext *cx) default: if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) - isJaegerCompileable = isInlineable = false; + isJaegerCompileable = false; + isJaegerInlineable = isIonInlineable = false; break; } diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 818e0c337e92..8f79d0208e5f 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -849,7 +849,8 @@ class ScriptAnalysis bool hasFunctionCalls_:1; bool modifiesArguments_:1; bool localsAliasStack_:1; - bool isInlineable:1; + bool isJaegerInlineable:1; + bool isIonInlineable:1; bool isJaegerCompileable:1; bool canTrackVars:1; bool hasLoops_:1; @@ -885,8 +886,10 @@ class ScriptAnalysis bool OOM() const { return outOfMemory; } bool failed() const { return hadFailure; } - bool inlineable() const { return isInlineable; } - bool inlineable(uint32_t argc) const { return isInlineable && argc == script_->function()->nargs; } + bool ionInlineable() const { return isIonInlineable; } + bool ionInlineable(uint32_t argc) const { return isIonInlineable && argc == script_->function()->nargs; } + bool jaegerInlineable() const { return isJaegerInlineable; } + bool jaegerInlineable(uint32_t argc) const { return isJaegerInlineable && argc == script_->function()->nargs; } bool jaegerCompileable() { return isJaegerCompileable; } /* Number of property read opcodes in the script. */ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 850d5f4aa81d..067a33b4266b 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -125,13 +125,12 @@ fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValu return false; #ifdef JS_ION - // If this script hasn't been compiled yet, make sure it will never - // be compiled. IonMonkey does not guarantee |f.arguments| can be + // Disabling compiling of this script in IonMonkey. + // IonMonkey does not guarantee |f.arguments| can be // fully recovered, so we try to mitigate observing this behavior by // detecting its use early. UnrootedScript script = iter.script(); - if (!script->hasAnyIonScript()) - ion::ForbidCompilation(cx, script); + ion::ForbidCompilation(cx, script); #endif vp.setObject(*argsobj); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 69f7398b1384..9304ea74ed24 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -353,7 +353,7 @@ mjit::Compiler::scanInlineCalls(uint32_t index, uint32_t depth) if (status != Compile_Okay) return status; - if (!script->analysis()->inlineable(argc)) { + if (!script->analysis()->jaegerInlineable(argc)) { okay = false; break; } diff --git a/js/src/vm/ArgumentsObject.cpp b/js/src/vm/ArgumentsObject.cpp index a2687feeebe3..9b2d2c115792 100644 --- a/js/src/vm/ArgumentsObject.cpp +++ b/js/src/vm/ArgumentsObject.cpp @@ -97,7 +97,7 @@ struct CopyStackIterArgs /* Define formals which are not part of the actuals. */ unsigned numActuals = iter_.numActualArgs(); unsigned numFormals = iter_.callee()->nargs; - if (numActuals < numFormals) { + if (numActuals < numFormals) { HeapValue *dst = dstBase + numActuals, *dstEnd = dstBase + numFormals; while (dst != dstEnd) (dst++)->init(UndefinedValue()); From ee5cb1d9351c4fbcb6f8f2f8b35f1f5c20342309 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 20 Dec 2012 22:33:26 -0800 Subject: [PATCH 112/217] Bug 809652 - Have SecurityWrapper::enter default to deny, and override SecurityWrapper::objectClassIs. r=jorendorff --- js/src/js.msg | 1 + js/src/jswrapper.cpp | 16 +++++++++++----- js/src/jswrapper.h | 2 ++ js/xpconnect/wrappers/FilteringWrapper.cpp | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/js/src/js.msg b/js/src/js.msg index 71fc75458736..bb1818a18714 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -376,3 +376,4 @@ MSG_DEF(JSMSG_CANT_SET_NW_NC, 322, 0, JSEXN_TYPEERR, "proxy can't succes MSG_DEF(JSMSG_CANT_SET_WO_SETTER, 323, 0, JSEXN_TYPEERR, "proxy can't succesfully set an accessor property without a setter") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 324, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 325, 2, JSEXN_TYPEERR, "{0} is a wrapper around {1}, but a direct reference is required") +MSG_DEF(JSMSG_UNWRAP_DENIED, 326, 0, JSEXN_ERR, "permission denied to unwrap object") diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index b8aef6d2ea11..9269669389d9 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -782,6 +782,16 @@ SecurityWrapper::SecurityWrapper(unsigned flags) template bool +SecurityWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, + Wrapper::Action act, bool *bp) +{ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNWRAP_DENIED); + *bp = false; + return false; +} + + template + bool SecurityWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) { @@ -796,11 +806,7 @@ template bool SecurityWrapper::objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) { - /* - * Let this through until compartment-per-global lets us have stronger - * invariants wrt document.domain (bug 714547). - */ - return Base::objectClassIs(obj, classValue, cx); + return false; } template diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 1a220c0bfed4..7e9add66d243 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -181,6 +181,8 @@ class JS_FRIEND_API(SecurityWrapper) : public Base public: SecurityWrapper(unsigned flags); + virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act, + bool *bp) MOZ_OVERRIDE; virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index a86225e36bee..2201f8f7c8ee 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -128,7 +128,7 @@ FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, return false; } *bp = true; - return Base::enter(cx, wrapper, id, act, bp); + return true; } #define SOW FilteringWrapper From 4c226c845b9a2664eb9c29e4977d0ae6c969fb37 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 20 Dec 2012 22:33:26 -0800 Subject: [PATCH 113/217] Bug 809652 - Add gross one-off predicates to jsfriendapi. r=jorendorff --- js/src/jsdate.cpp | 47 +++++++++++++++++++++++++++++++++++++++++ js/src/jsfriendapi.h | 6 ++++++ js/src/jstypedarray.cpp | 17 +++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index 7b7cc83bbdb8..18ad814fec91 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3241,3 +3241,50 @@ js_DateGetMsecSinceEpoch(RawObject obj) return obj->isDate() ? obj->getDateUTCTime().toNumber() : 0; } + +static const NativeImpl sReadOnlyDateMethods[] = { + date_getTime_impl, + date_getYear_impl, + date_getFullYear_impl, + date_getUTCFullYear_impl, + date_getMonth_impl, + date_getUTCMonth_impl, + date_getDate_impl, + date_getUTCDate_impl, + date_getDay_impl, + date_getUTCDay_impl, + date_getHours_impl, + date_getUTCHours_impl, + date_getMinutes_impl, + date_getUTCMinutes_impl, + date_getUTCSeconds_impl, + date_getUTCMilliseconds_impl, + date_getTimezoneOffset_impl, + date_toGMTString_impl, + date_toISOString_impl, + date_toLocaleString_impl, + date_toLocaleDateString_impl, + date_toLocaleTimeString_impl, + date_toLocaleFormat_impl, + date_toTimeString_impl, + date_toDateString_impl, + date_toSource_impl, + date_toString_impl, + date_valueOf_impl +}; + +JS_FRIEND_API(bool) +js::IsReadOnlyDateMethod(IsAcceptableThis test, NativeImpl method) +{ + /* Avoid a linear search in the common case by checking the |this| test. */ + if (test != IsDate) + return false; + + /* Linear search, comparing function pointers. */ + unsigned max = sizeof(sReadOnlyDateMethods) / sizeof(sReadOnlyDateMethods[0]); + for (unsigned i = 0; i < max; ++i) { + if (method == sReadOnlyDateMethods[i]) + return true; + } + return false; +} diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 675ef2412d0c..172b2e988697 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1549,6 +1549,12 @@ IdToJsval(jsid id) return IdToValue(id); } +extern JS_FRIEND_API(bool) +IsReadOnlyDateMethod(JS::IsAcceptableThis test, JS::NativeImpl method); + +extern JS_FRIEND_API(bool) +IsTypedArrayThisCheck(JS::IsAcceptableThis test); + } /* namespace js */ #endif /* jsfriendapi_h___ */ diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 392fbb7b5eaf..9c77e937c4aa 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -3424,6 +3424,23 @@ Class TypedArray::protoClasses[TYPE_MAX] = { IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray) }; +#define CHECK(t, a) { if (t == a::IsThisClass) return true; } +JS_FRIEND_API(bool) +js::IsTypedArrayThisCheck(JS::IsAcceptableThis test) +{ + CHECK(test, Int8Array); + CHECK(test, Uint8Array); + CHECK(test, Int16Array); + CHECK(test, Uint16Array); + CHECK(test, Int32Array); + CHECK(test, Uint32Array); + CHECK(test, Float32Array); + CHECK(test, Float64Array); + CHECK(test, Uint8ClampedArray); + return false; +} +#undef CHECK + static JSObject * InitArrayBufferClass(JSContext *cx) { From f681aaffe96323b98c522f768106453b9af681b8 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 20 Dec 2012 22:33:26 -0800 Subject: [PATCH 114/217] Bug 809652 - Deny nativeCall for SecurityWrapper except under specific circumstances. r=jorendorff --- js/src/jswrapper.cpp | 7 ++----- js/src/jswrapper.h | 7 +++++++ js/xpconnect/wrappers/AccessCheck.cpp | 7 +++++++ js/xpconnect/wrappers/AccessCheck.h | 13 +++++++++++++ js/xpconnect/wrappers/FilteringWrapper.cpp | 10 ++++++++++ js/xpconnect/wrappers/FilteringWrapper.h | 2 ++ 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 9269669389d9..6fc4e26234f4 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -795,11 +795,8 @@ SecurityWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, SecurityWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args) { - /* - * Let this through until compartment-per-global lets us have stronger - * invariants wrt document.domain (bug 714547). - */ - return Base::nativeCall(cx, test, impl, args); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNWRAP_DENIED); + return false; } template diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 7e9add66d243..0b20a26232c9 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -187,6 +187,13 @@ class JS_FRIEND_API(SecurityWrapper) : public Base CallArgs args) MOZ_OVERRIDE; virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE; virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE; + + /* + * Allow our subclasses to select the superclass behavior they want without + * needing to specify an exact superclass. + */ + typedef Base Permissive; + typedef SecurityWrapper Restrictive; }; typedef SecurityWrapper SameCompartmentSecurityWrapper; diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index c8bdb3c0fd09..c7a968c088e3 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -446,6 +446,13 @@ ExposedPropertiesOnly::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper: return true; } +bool +ExposedPropertiesOnly::allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, + JS::NativeImpl impl) +{ + return js::IsReadOnlyDateMethod(test, impl) || js::IsTypedArrayThisCheck(test); +} + bool ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act) { diff --git a/js/xpconnect/wrappers/AccessCheck.h b/js/xpconnect/wrappers/AccessCheck.h index 575d60d585a6..2861b1283996 100644 --- a/js/xpconnect/wrappers/AccessCheck.h +++ b/js/xpconnect/wrappers/AccessCheck.h @@ -51,6 +51,11 @@ struct OnlyIfSubjectIsSystem : public Policy { AccessCheck::deny(cx, id); return false; } + + static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) + { + return AccessCheck::isSystemOnlyAccessPermitted(cx); + } }; // This policy only permits access to properties that are safe to be used @@ -63,6 +68,10 @@ struct CrossOriginAccessiblePropertiesOnly : public Policy { AccessCheck::deny(cx, id); return false; } + static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) + { + return false; + } }; // This policy only permits access to properties if they appear in the @@ -78,6 +87,7 @@ struct ExposedPropertiesOnly : public Policy { AccessCheck::deny(cx, id); return false; } + static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl); }; // Components specific policy @@ -88,6 +98,9 @@ struct ComponentsObjectPolicy : public Policy { AccessCheck::deny(cx, id); return false; } + static bool allowNativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl) { + return false; + } }; } diff --git a/js/xpconnect/wrappers/FilteringWrapper.cpp b/js/xpconnect/wrappers/FilteringWrapper.cpp index 2201f8f7c8ee..1e2ed93bbf52 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.cpp +++ b/js/xpconnect/wrappers/FilteringWrapper.cpp @@ -113,6 +113,16 @@ FilteringWrapper::iterate(JSContext *cx, JSObject *wrapper, unsign return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); } +template +bool +FilteringWrapper::nativeCall(JSContext *cx, JS::IsAcceptableThis test, + JS::NativeImpl impl, JS::CallArgs args) +{ + if (Policy::allowNativeCall(cx, test, impl)) + return Base::Permissive::nativeCall(cx, test, impl, args); + return Base::Restrictive::nativeCall(cx, test, impl, args); +} + template bool FilteringWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, diff --git a/js/xpconnect/wrappers/FilteringWrapper.h b/js/xpconnect/wrappers/FilteringWrapper.h index c6ee3f59c2ca..a6784158fe6b 100644 --- a/js/xpconnect/wrappers/FilteringWrapper.h +++ b/js/xpconnect/wrappers/FilteringWrapper.h @@ -25,6 +25,8 @@ class FilteringWrapper : public Base { virtual bool enumerate(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) MOZ_OVERRIDE; virtual bool keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props) MOZ_OVERRIDE; virtual bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, js::Value *vp) MOZ_OVERRIDE; + virtual bool nativeCall(JSContext *cx, JS::IsAcceptableThis test, JS::NativeImpl impl, + JS::CallArgs args) MOZ_OVERRIDE; virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act, bool *bp) MOZ_OVERRIDE; From e4c1ae9fc4a99f8edaa817a2af01e6e15e7ca38b Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Thu, 13 Dec 2012 12:20:00 -0500 Subject: [PATCH 115/217] Bug 807757 part PSM - Remove nsIProfileChangeStatus and related veto notifications from PSM r=bsmith --HG-- extra : rebase_source : a4611953352684c48913501d26e8ce782ba04f8f --- .../en-US/chrome/pipnss/pipnss.properties | 2 - security/manager/ssl/src/nsNSSComponent.cpp | 68 ++----------------- security/manager/ssl/src/nsNSSComponent.h | 3 +- 3 files changed, 5 insertions(+), 68 deletions(-) diff --git a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties index 297cfd103829..5f36bf1a9b4c 100755 --- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties +++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties @@ -349,8 +349,6 @@ CrlImportFailureNetworkProblem=Download of the CRL failed due to Network problem CrlImportFailureReasonUnknown=Error Importing CRL to local Database. Error Code: CrlImportFailure2=Please ask your system administrator for assistance. NSSInitProblemX=Could not initialize the application's security component. The most likely cause is problems with files in your application's profile directory. Please check that this directory has no read/write restrictions and your hard disk is not full or close to full. It is recommended that you exit the application and fix the problem. If you continue to use this session, you might see incorrect application behaviour when accessing security features. -ProfileSwitchSocketsStillActive=The operation cannot be completed because of an internal failure. A secure network communication has not been cleaned up correctly. -ProfileSwitchCryptoUIActive=This operation is impossible at the current time. Please complete the operation that requests your attention in one of the other open windows. VerifyExpired= VerifyRevoked= VerifyNotTrusted= diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp index aabe6fab9789..8e9bd586597b 100644 --- a/security/manager/ssl/src/nsNSSComponent.cpp +++ b/security/manager/ssl/src/nsNSSComponent.cpp @@ -24,7 +24,6 @@ #include "nsDirectoryServiceDefs.h" #include "nsIX509Cert.h" #include "nsIX509CertDB.h" -#include "nsIProfileChangeStatus.h" #include "nsNSSCertificate.h" #include "nsNSSHelper.h" #include "nsSmartCardMonitor.h" @@ -1831,7 +1830,7 @@ nsNSSComponent::InitializeNSS(bool showWarningBox) return NS_OK; } -nsresult +void nsNSSComponent::ShutdownNSS() { // Can be called both during init and profile change, @@ -1840,7 +1839,6 @@ nsNSSComponent::ShutdownNSS() PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::ShutdownNSS\n")); MutexAutoLock lock(mutex); - nsresult rv = NS_OK; if (hashTableCerts) { PL_HashTableEnumerateEntries(hashTableCerts, certHashtable_clearEntry, 0); @@ -1868,14 +1866,11 @@ nsNSSComponent::ShutdownNSS() EnsureNSSInitialized(nssShutdown); if (SECSuccess != ::NSS_Shutdown()) { PR_LOG(gPIPNSSLog, PR_LOG_ALWAYS, ("NSS SHUTDOWN FAILURE\n")); - rv = NS_ERROR_FAILURE; } else { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS shutdown =====>> OK <<=====\n")); } } - - return rv; } NS_IMETHODIMP @@ -2133,9 +2128,7 @@ nsNSSComponent::RandomUpdate(void *entropy, int32_t bufLen) #define PROFILE_CHANGE_NET_TEARDOWN_TOPIC "profile-change-net-teardown" #define PROFILE_CHANGE_NET_RESTORE_TOPIC "profile-change-net-restore" -#define PROFILE_APPROVE_CHANGE_TOPIC "profile-approve-change" #define PROFILE_CHANGE_TEARDOWN_TOPIC "profile-change-teardown" -#define PROFILE_CHANGE_TEARDOWN_VETO_TOPIC "profile-change-teardown-veto" #define PROFILE_BEFORE_CHANGE_TOPIC "profile-before-change" #define PROFILE_DO_CHANGE_TOPIC "profile-do-change" @@ -2143,16 +2136,10 @@ NS_IMETHODIMP nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData) { - if (nsCRT::strcmp(aTopic, PROFILE_APPROVE_CHANGE_TOPIC) == 0) { - DoProfileApproveChange(aSubject); - } - else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_TEARDOWN_TOPIC) == 0) { + if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_TEARDOWN_TOPIC) == 0) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("in PSM code, receiving change-teardown\n")); DoProfileChangeTeardown(aSubject); } - else if (nsCRT::strcmp(aTopic, PROFILE_CHANGE_TEARDOWN_VETO_TOPIC) == 0) { - mShutdownObjectList->allowUI(); - } else if (nsCRT::strcmp(aTopic, PROFILE_BEFORE_CHANGE_TOPIC) == 0) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("receiving profile change topic\n")); DoProfileBeforeChange(aSubject); @@ -2166,7 +2153,6 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic, // it again. We use the same cleanup functionality used when switching // profiles. The order of function calls must correspond to the order // of notifications sent by Profile Manager (nsProfile). - DoProfileApproveChange(aSubject); DoProfileChangeNetTeardown(); DoProfileChangeTeardown(aSubject); DoProfileBeforeChange(aSubject); @@ -2188,10 +2174,6 @@ nsNSSComponent::Observe(nsISupports *aSubject, const char *aTopic, if (needsInit) { if (NS_FAILED(InitializeNSS(false))) { // do not show a warning box on failure PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to Initialize NSS after profile switch.\n")); - nsCOMPtr status = do_QueryInterface(aSubject); - if (status) { - status->ChangeFailed(); - } } } @@ -2377,9 +2359,7 @@ nsNSSComponent::RegisterObservers() observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); - observerService->AddObserver(this, PROFILE_APPROVE_CHANGE_TOPIC, false); observerService->AddObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC, false); - observerService->AddObserver(this, PROFILE_CHANGE_TEARDOWN_VETO_TOPIC, false); observerService->AddObserver(this, PROFILE_BEFORE_CHANGE_TOPIC, false); observerService->AddObserver(this, PROFILE_DO_CHANGE_TOPIC, false); observerService->AddObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC, false); @@ -2402,9 +2382,7 @@ nsNSSComponent::DeregisterObservers() observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - observerService->RemoveObserver(this, PROFILE_APPROVE_CHANGE_TOPIC); observerService->RemoveObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC); - observerService->RemoveObserver(this, PROFILE_CHANGE_TEARDOWN_VETO_TOPIC); observerService->RemoveObserver(this, PROFILE_BEFORE_CHANGE_TOPIC); observerService->RemoveObserver(this, PROFILE_DO_CHANGE_TOPIC); observerService->RemoveObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC); @@ -2444,23 +2422,6 @@ nsNSSComponent::RememberCert(CERTCertificate *cert) return NS_OK; } -static const char PROFILE_SWITCH_CRYPTO_UI_ACTIVE[] = - "ProfileSwitchCryptoUIActive"; -static const char PROFILE_SWITCH_SOCKETS_STILL_ACTIVE[] = - "ProfileSwitchSocketsStillActive"; - -void -nsNSSComponent::DoProfileApproveChange(nsISupports* aSubject) -{ - if (mShutdownObjectList->isUIActive()) { - ShowAlertFromStringBundle(PROFILE_SWITCH_CRYPTO_UI_ACTIVE); - nsCOMPtr status = do_QueryInterface(aSubject); - if (status) { - status->VetoChange(); - } - } -} - void nsNSSComponent::DoProfileChangeNetTeardown() { @@ -2472,23 +2433,7 @@ nsNSSComponent::DoProfileChangeNetTeardown() void nsNSSComponent::DoProfileChangeTeardown(nsISupports* aSubject) { - bool callVeto = false; - - if (!mShutdownObjectList->ifPossibleDisallowUI()) { - callVeto = true; - ShowAlertFromStringBundle(PROFILE_SWITCH_CRYPTO_UI_ACTIVE); - } - else if (mShutdownObjectList->areSSLSocketsActive()) { - callVeto = true; - ShowAlertFromStringBundle(PROFILE_SWITCH_SOCKETS_STILL_ACTIVE); - } - - if (callVeto) { - nsCOMPtr status = do_QueryInterface(aSubject); - if (status) { - status->VetoChange(); - } - } + mShutdownObjectList->ifPossibleDisallowUI(); } void @@ -2512,12 +2457,7 @@ nsNSSComponent::DoProfileBeforeChange(nsISupports* aSubject) StopCRLUpdateTimer(); if (needsCleanup) { - if (NS_FAILED(ShutdownNSS())) { - nsCOMPtr status = do_QueryInterface(aSubject); - if (status) { - status->ChangeFailed(); - } - } + ShutdownNSS(); } mShutdownObjectList->allowUI(); } diff --git a/security/manager/ssl/src/nsNSSComponent.h b/security/manager/ssl/src/nsNSSComponent.h index b47b989c21f5..6b71f3849358 100644 --- a/security/manager/ssl/src/nsNSSComponent.h +++ b/security/manager/ssl/src/nsNSSComponent.h @@ -267,7 +267,7 @@ public: private: nsresult InitializeNSS(bool showWarningBox); - nsresult ShutdownNSS(); + void ShutdownNSS(); #ifdef XP_MACOSX void TryCFM2MachOMigration(nsIFile *cfmPath, nsIFile *machoPath); @@ -291,7 +291,6 @@ private: // Methods that we use to handle the profile change notifications (and to // synthesize a full profile change when we're just doing a profile startup): - void DoProfileApproveChange(nsISupports* aSubject); void DoProfileChangeNetTeardown(); void DoProfileChangeTeardown(nsISupports* aSubject); void DoProfileBeforeChange(nsISupports* aSubject); From ab775a4ccfba55b0541eabc2fa1e8bdd0031518f Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Thu, 13 Dec 2012 12:20:56 -0500 Subject: [PATCH 116/217] Bug 807757 main part - remove nsIProfileChangeStatus, nsIProfile and remnants of code to support them, r=glandium --HG-- rename : profile/public/nsIProfileChangeStatus.idl => profile/public/notifications.txt extra : rebase_source : 89db8a63effc2eab589a184a2b686cfcca3f8f63 --- embedding/tests/winEmbed/WebBrowserChrome.cpp | 1 - .../pref/autoconfig/src/nsAutoConfig.cpp | 20 +--- extensions/pref/autoconfig/src/nsAutoConfig.h | 2 +- .../public/nsProfileDirServiceProvider.h | 2 +- profile/public/Makefile.in | 5 - profile/public/notifications.txt | 61 ++++++++++ profile/public/nsIProfile.idl | 54 --------- profile/public/nsIProfileChangeStatus.idl | 109 ------------------ toolkit/components/startup/nsAppStartup.cpp | 1 - toolkit/xre/nsXREDirProvider.cpp | 34 +----- 10 files changed, 68 insertions(+), 221 deletions(-) create mode 100644 profile/public/notifications.txt delete mode 100644 profile/public/nsIProfile.idl delete mode 100644 profile/public/nsIProfileChangeStatus.idl diff --git a/embedding/tests/winEmbed/WebBrowserChrome.cpp b/embedding/tests/winEmbed/WebBrowserChrome.cpp index 33bbd11e6c89..32a38655406c 100644 --- a/embedding/tests/winEmbed/WebBrowserChrome.cpp +++ b/embedding/tests/winEmbed/WebBrowserChrome.cpp @@ -45,7 +45,6 @@ #include "nsIURI.h" #include "nsIWebProgress.h" #include "nsCWebBrowser.h" -#include "nsIProfileChangeStatus.h" // Glue APIs (not frozen, but safe to use because they are statically linked) #include "nsComponentManagerUtils.h" diff --git a/extensions/pref/autoconfig/src/nsAutoConfig.cpp b/extensions/pref/autoconfig/src/nsAutoConfig.cpp index 6ac4fc8def47..526c5a6d1573 100644 --- a/extensions/pref/autoconfig/src/nsAutoConfig.cpp +++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp @@ -14,7 +14,6 @@ #include "nsThreadUtils.h" #include "nsAppDirectoryServiceDefs.h" #include "prmem.h" -#include "nsIProfile.h" #include "nsIObserverService.h" #include "nsLiteralString.h" #include "nsIPromptService.h" @@ -180,21 +179,6 @@ NS_IMETHODIMP nsAutoConfig::Observe(nsISupports *aSubject, nsresult rv = NS_OK; if (!nsCRT::strcmp(aTopic, "profile-after-change")) { - // Getting the current profile name since we already have the - // pointer to the object. - nsCOMPtr profile = do_QueryInterface(aSubject); - if (profile) { - nsXPIDLString profileName; - rv = profile->GetCurrentProfile(getter_Copies(profileName)); - if (NS_SUCCEEDED(rv)) { - // setting the member variable to the current profile name - CopyUTF16toUTF8(profileName, mCurrProfile); - } - else { - NS_WARNING("nsAutoConfig::GetCurrentProfile() failed"); - } - } - // We will be calling downloadAutoConfig even if there is no profile // name. Nothing will be passed as a parameter to the URL and the // default case will be picked up by the script. @@ -496,8 +480,8 @@ nsresult nsAutoConfig::getEmailAddr(nsACString & emailAddr) getter_Copies(prefValue)); if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty()) emailAddr = prefValue; - else if (NS_FAILED(PromptForEMailAddress(emailAddr)) && (!mCurrProfile.IsEmpty())) - emailAddr = mCurrProfile; + else + PromptForEMailAddress(emailAddr); } return NS_OK; diff --git a/extensions/pref/autoconfig/src/nsAutoConfig.h b/extensions/pref/autoconfig/src/nsAutoConfig.h index 8524345a7bf7..647efbce0a3a 100644 --- a/extensions/pref/autoconfig/src/nsAutoConfig.h +++ b/extensions/pref/autoconfig/src/nsAutoConfig.h @@ -40,7 +40,7 @@ class nsAutoConfig : public nsIAutoConfig, nsresult writeFailoverFile(); nsresult getEmailAddr(nsACString & emailAddr); nsresult PromptForEMailAddress(nsACString &emailAddress); - nsCString mBuf, mCurrProfile; + nsCString mBuf; nsCOMPtr mPrefBranch; bool mLoaded; nsCOMPtr mTimer; diff --git a/profile/dirserviceprovider/public/nsProfileDirServiceProvider.h b/profile/dirserviceprovider/public/nsProfileDirServiceProvider.h index 6c4ed898692a..bf00523923a5 100644 --- a/profile/dirserviceprovider/public/nsProfileDirServiceProvider.h +++ b/profile/dirserviceprovider/public/nsProfileDirServiceProvider.h @@ -104,7 +104,7 @@ protected: * * @param aNotifyObservers If true, will send out profile startup * notifications when the profile directory is set. - * See nsIProfileChangeStatus. + * See notifications.txt */ nsresult NS_NewProfileDirServiceProvider(bool aNotifyObservers, diff --git a/profile/public/Makefile.in b/profile/public/Makefile.in index 859739eb56e0..64b7668eaa15 100644 --- a/profile/public/Makefile.in +++ b/profile/public/Makefile.in @@ -12,11 +12,6 @@ include $(DEPTH)/config/autoconf.mk MODULE = profile -SDK_XPIDLSRCS = \ - nsIProfile.idl \ - nsIProfileChangeStatus.idl \ - $(NULL) - XPIDLSRCS = \ nsIProfileUnlocker.idl \ $(NULL) diff --git a/profile/public/notifications.txt b/profile/public/notifications.txt new file mode 100644 index 000000000000..eab1e2c347ae --- /dev/null +++ b/profile/public/notifications.txt @@ -0,0 +1,61 @@ +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/. + +nsIObserver topics for profile changing. Profile changing happens in phases +in the order given below. An observer may register separately for each phase +of the process depending on its needs. + +"profile-change-teardown" + All async activity must be stopped in this phase. Typically, + the application level observer will close all open windows. + This is the last phase in which the subject's vetoChange() + method may still be called. + The next notification will be either + profile-change-teardown-veto or profile-before-change. + +"profile-before-change" + Called before the profile has changed. Use this notification + to prepare for the profile going away. If a component is + holding any state which needs to be flushed to a profile-relative + location, it should be done here. + +"profile-do-change" + Called after the profile has changed. Do the work to + respond to having a new profile. Any change which + affects others must be done in this phase. + +"profile-after-change" + Called after the profile has changed. Use this notification + to make changes that are dependent on what some other listener + did during its profile-do-change. For example, to respond to + new preferences. + +"profile-initial-state" + Called after all phases of a change have completed. Typically + in this phase, an application level observer will open a new window. + +Contexts for profile changes. These are passed as the someData param to the +observer's Observe() method. + +"startup" + Going from no profile to a profile. + The following topics happen in this context: + profile-do-change + profile-after-change + +"shutdown-persist" + The user is logging out and whatever data the observer stores + for the current profile should be released from memory and + saved to disk. + The following topics happen in this context: + profile-change-net-teardown + profile-change-teardown + profile-before-change + +See https://wiki.mozilla.org/XPCOM_Shutdown for more details about the shutdown +process. + +NOTE: Long ago there was be a "shutdown-cleanse" version of shutdown which was +intended to clear profile data. This is no longer sent and observer code should +remove support for it. diff --git a/profile/public/nsIProfile.idl b/profile/public/nsIProfile.idl deleted file mode 100644 index 2c0a6b9d39dd..000000000000 --- a/profile/public/nsIProfile.idl +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsISupports.idl" -#include "nsIFile.idl" - -%{C++ - -#define NS_PROFILE_CID \ - { /* {02b0625b-e7f3-11d2-9f5a-006008a6efe9} */ \ - 0x02b0625b, \ - 0xe7f3, \ - 0x11d2, \ - { 0x9f, 0x5a, 0x00, 0x60, 0x08, 0xa6, 0xef, 0xe9 } \ - } - -#define NS_PROFILE_CONTRACTID \ - "@mozilla.org/profile/manager;1" - -#define NS_PROFILE_STARTUP_CATEGORY \ - "profile-startup-category" -%} - -/** - * nsIProfile - * - * @version 1.0 - */ - -[scriptable, uuid(02b0625a-e7f3-11d2-9f5a-006008a6efe9)] -interface nsIProfile : nsISupports { - - readonly attribute long profileCount; - void getProfileList(out unsigned long length, [retval, array, size_is(length)] out wstring profileNames); - boolean profileExists(in wstring profileName); - - attribute wstring currentProfile; - - const unsigned long SHUTDOWN_PERSIST = 0x00000001; - const unsigned long SHUTDOWN_CLEANSE = 0x00000002; - - void shutDownCurrentProfile(in unsigned long shutDownType); - - void createNewProfile(in wstring profileName, - in wstring nativeProfileDir, - in wstring langcode, - in boolean useExistingDir); - - void renameProfile(in wstring oldName, in wstring newName); - void deleteProfile(in wstring name, in boolean canDeleteFiles); - void cloneProfile(in wstring profileName); -}; diff --git a/profile/public/nsIProfileChangeStatus.idl b/profile/public/nsIProfileChangeStatus.idl deleted file mode 100644 index 44539124a728..000000000000 --- a/profile/public/nsIProfileChangeStatus.idl +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* 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/. */ - -#include "nsISupports.idl" - -%{C++ - - /** - * nsIObserver topics for profile changing. Profile changing happens in phases - * in the order given below. An observer may register separately for each phase - * of the process depending on its needs. The subject passed to the observer's - * Observe() method can be QI'd to an nsIProfileChangeStatus. - * - * "profile-approve-change" - * Called before a profile change is attempted. Typically, - * the application level observer will ask the user if - * he/she wants to stop all network activity, close all open - * windows, etc. If the user says NO, the observer should - * call the subject's vetoChange(). If any observer does - * this, the profile will not be changed. - * - * "profile-change-teardown" - * All async activity must be stopped in this phase. Typically, - * the application level observer will close all open windows. - * This is the last phase in which the subject's vetoChange() - * method may still be called. - * The next notification will be either - * profile-change-teardown-veto or profile-before-change. - * - * "profile-change-teardown-veto" - * This notification will only be sent, if the profile change - * was vetoed during the profile-change-teardown phase. - * This allows components to bring back required resources, - * that were tore down on profile-change-teardown. - * - * "profile-before-change" - * Called before the profile has changed. Use this notification - * to prepare for the profile going away. If a component is - * holding any state which needs to be flushed to a profile-relative - * location, it should be done here. - * - * "profile-do-change" - * Called after the profile has changed. Do the work to - * respond to having a new profile. Any change which - * affects others must be done in this phase. - * - * "profile-after-change" - * Called after the profile has changed. Use this notification - * to make changes that are dependent on what some other listener - * did during its profile-do-change. For example, to respond to - * new preferences. - * - * "profile-initial-state" - * Called after all phases of a change have completed. Typically - * in this phase, an application level observer will open a new window. - * - * Contexts for profile changes. These are passed as the someData param to the - * observer's Observe() method. - - * "startup" - * Going from no profile to a profile. - * - * The following topics happen in this context: - * profile-do-change - * profile-after-change - * - * "shutdown-persist" - * The user is logging out and whatever data the observer stores - * for the current profile should be released from memory and - * saved to disk. - * - * "shutdown-cleanse" - * The user is logging out and whatever data the observer stores - * for the current profile should be released from memory and - * deleted from disk. - * - * The following topics happen in both shutdown contexts: - * profile-approve-change - * profile-change-teardown - * profile-before-change - * - * "switch" - * Going from one profile to another. - * - * All of the above topics happen in a profile switch. - * - */ -%} - - -[scriptable, uuid(2f977d43-5485-11d4-87e2-0010a4e75ef2)] -interface nsIProfileChangeStatus : nsISupports { - - void vetoChange(); - - /** - * Called by a profile change observer when a fatal error - * occurred during the attempt to switch the profile. - * - * The profile should be considered in an unsafe condition, - * and the profile manager should inform the user and - * exit immediately. - * - */ - void changeFailed(); - -}; diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp index 7d40e2857b3b..f31a88aaf286 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -12,7 +12,6 @@ #include "nsIObserverService.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" -#include "nsIProfileChangeStatus.h" #include "nsIPromptService.h" #include "nsIStringBundle.h" #include "nsISupportsPrimitives.h" diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index 144e5e6e791a..23e6d4a8abd0 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -15,7 +15,6 @@ #include "nsIFile.h" #include "nsIObserver.h" #include "nsIObserverService.h" -#include "nsIProfileChangeStatus.h" #include "nsISimpleEnumerator.h" #include "nsIToolkitChromeRegistry.h" @@ -835,32 +834,6 @@ nsXREDirProvider::DoStartup() return NS_OK; } -class ProfileChangeStatusImpl MOZ_FINAL : public nsIProfileChangeStatus -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPROFILECHANGESTATUS - ProfileChangeStatusImpl() { } -private: - ~ProfileChangeStatusImpl() { } -}; - -NS_IMPL_ISUPPORTS1(ProfileChangeStatusImpl, nsIProfileChangeStatus) - -NS_IMETHODIMP -ProfileChangeStatusImpl::VetoChange() -{ - NS_ERROR("Can't veto change!"); - return NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -ProfileChangeStatusImpl::ChangeFailed() -{ - NS_ERROR("Profile change cancellation."); - return NS_ERROR_FAILURE; -} - void nsXREDirProvider::DoShutdown() { @@ -869,11 +842,10 @@ nsXREDirProvider::DoShutdown() mozilla::services::GetObserverService(); NS_ASSERTION(obsSvc, "No observer service?"); if (obsSvc) { - nsCOMPtr cs = new ProfileChangeStatusImpl(); static const PRUnichar kShutdownPersist[] = {'s','h','u','t','d','o','w','n','-','p','e','r','s','i','s','t','\0'}; - obsSvc->NotifyObservers(cs, "profile-change-net-teardown", kShutdownPersist); - obsSvc->NotifyObservers(cs, "profile-change-teardown", kShutdownPersist); + obsSvc->NotifyObservers(nullptr, "profile-change-net-teardown", kShutdownPersist); + obsSvc->NotifyObservers(nullptr, "profile-change-teardown", kShutdownPersist); // Phase 2c: Now that things are torn down, force JS GC so that things which depend on // resources which are about to go away in "profile-before-change" are destroyed first. @@ -889,7 +861,7 @@ nsXREDirProvider::DoShutdown() } // Phase 3: Notify observers of a profile change - obsSvc->NotifyObservers(cs, "profile-before-change", kShutdownPersist); + obsSvc->NotifyObservers(nullptr, "profile-before-change", kShutdownPersist); } mProfileNotified = false; } From 77cd14edf423e30555f1b80485f86e04b636badb Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Fri, 21 Dec 2012 11:40:52 -0500 Subject: [PATCH 117/217] Bug 823502 - ship dom_system.xpt for Android, r=gbrown, r=mfinkle --HG-- extra : rebase_source : ebb771ba851a05e37cda7ec3e4befdbbcf05c44c --- mobile/android/installer/package-manifest.in | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index efff6fada495..211ec10965ca 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -131,6 +131,7 @@ @BINPATH@/components/dom_sms.xpt @BINPATH@/components/dom_storage.xpt @BINPATH@/components/dom_stylesheets.xpt +@BINPATH@/components/dom_system.xpt @BINPATH@/components/dom_threads.xpt @BINPATH@/components/dom_traversal.xpt @BINPATH@/components/dom_views.xpt From 28e38bbdf3955ac6684d84458c4f533b0d211626 Mon Sep 17 00:00:00 2001 From: David Rajchenbach-Teller Date: Fri, 21 Dec 2012 11:41:15 -0500 Subject: [PATCH 118/217] Bug 760036 - move SearchService metadata I/O off the main thread, r=enndeakin --HG-- extra : rebase_source : 1c5f12f3730f4631a706a7b590ce6807f84fa62c --- toolkit/components/search/nsSearchService.js | 502 ++++++++++++++----- 1 file changed, 370 insertions(+), 132 deletions(-) diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index 2e9f32ab443a..cc23ca83e7e6 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -8,9 +8,21 @@ const Cr = Components.results; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/commonjs/promise/core.js"); XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "OS", + "resource://gre/modules/osfile.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); + +// A text encoder to UTF8, used whenever we commit the +// engine metadata to disk. +XPCOMUtils.defineLazyGetter(this, "gEncoder", + function() { + return new TextEncoder(); + }); const PERMS_FILE = 0644; const PERMS_DIRECTORY = 0755; @@ -265,6 +277,71 @@ function FAIL(message, resultCode) { throw Components.Exception(message, resultCode || Cr.NS_ERROR_INVALID_ARG); } +/** + * Utilities for dealing with promises and Task.jsm + */ +const TaskUtils = { + /** + * Add logging to a promise. + * + * @param {Promise} promise + * @return {Promise} A promise behaving as |promise|, but with additional + * logging in case of uncaught error. + */ + captureErrors: function captureErrors(promise) { + return promise.then( + null, + function onError(reason) { + LOG("Uncaught asynchronous error: " + reason + " at\n" + reason.stack); + throw reason; + } + ); + }, + /** + * Spawn a new Task from a generator. + * + * This function behaves as |Task.spawn|, with the exception that it + * adds logging in case of uncaught error. For more information, see + * the documentation of |Task.jsm|. + * + * @param {generator} gen Some generator. + * @return {Promise} A promise built from |gen|, with the same semantics + * as |Task.spawn(gen)|. + */ + spawn: function spawn(gen) { + return this.captureErrors(Task.spawn(gen)); + }, + /** + * Execute a mozIStorage statement asynchronously, wrapping the + * result in a promise. + * + * @param {mozIStorageStaement} statement A statement to be executed + * asynchronously. The semantics are the same as these of |statement.execute|. + * @param {function*} onResult A callback, called for each successive result. + * + * @return {Promise} A promise, resolved successfully if |statement.execute| + * succeeds, rejected if it fails. + */ + executeStatement: function executeStatement(statement, onResult) { + let deferred = Promise.defer(); + onResult = onResult || function() {}; + statement.executeAsync({ + handleResult: onResult, + handleError: function handleError(aError) { + deferred.reject(aError); + }, + handleCompletion: function handleCompletion(aReason) { + statement.finalize(); + // Note that, in case of error, deferred.reject(aError) + // has already been called by this point, so the call to + // |deferred.resolve| is simply ignored. + deferred.resolve(aReason); + } + }); + return deferred.promise; + } +}; + /** * Ensures an assertion is met before continuing. Should be used to indicate * fatal errors. @@ -2420,13 +2497,7 @@ function SearchService() { if (getBoolPref(BROWSER_SEARCH_PREF + "log", false)) LOG = DO_LOG; - /** - * If initialization is not complete yet, an array of - * |nsIBrowserSearchInitObserver| expecting the result of the end of - * initialization. - * Once initialization is complete, |null|. - */ - this._initObservers = []; + this._initObservers = Promise.defer(); } SearchService.prototype = { @@ -2442,15 +2513,9 @@ SearchService.prototype = { _ensureInitialized: function SRCH_SVC__ensureInitialized() { if (gInitialized) { if (!Components.isSuccessCode(this._initRV)) { + LOG("_ensureInitialized: failure"); throw this._initRV; } - - // Ensure that the following calls to |_ensureInitialized| can be inlined - // to a noop. Note that we could do this at the end of both |_init| and - // |_syncInit|, to save one call to a non-empty |_ensureInitialized|, but - // this would complicate code. - delete this._ensureInitialized; - this._ensureInitialized = function SRCH_SVC__ensureInitializedDone() { }; return; } @@ -2464,6 +2529,7 @@ SearchService.prototype = { //Components.utils.reportError(warning); LOG(warning); + engineMetadataService.syncInit(); this._syncInit(); if (!Components.isSuccessCode(this._initRV)) { throw this._initRV; @@ -2471,11 +2537,11 @@ SearchService.prototype = { }, // Synchronous implementation of the initializer. - // Used as by |_ensureInitialized| as a fallback if initialization is not + // Used by |_ensureInitialized| as a fallback if initialization is not // complete. In this implementation, it is also used by |init|. _syncInit: function SRCH_SVC__syncInit() { try { - this._loadEngines(); + this._syncLoadEngines(); } catch (ex) { this._initRV = Cr.NS_ERROR_FAILURE; LOG("_syncInit: failure loading engines: " + ex); @@ -2483,16 +2549,8 @@ SearchService.prototype = { this._addObservers(); gInitialized = true; - - // Notify all of the init observers - this._initObservers.forEach(function (observer) { - try { - observer.onInitComplete(this._initRV); - } catch (x) { - LOG("nsIBrowserInitObserver failed with error " + x); - } - }, this); - this._initObservers = null; + this._initObservers.resolve(this._initRV); + LOG("_syncInit: Completed _syncInit"); }, _engines: { }, @@ -2594,8 +2652,8 @@ SearchService.prototype = { } }, - _loadEngines: function SRCH_SVC__loadEngines() { - LOG("_loadEngines: start"); + _syncLoadEngines: function SRCH_SVC__syncLoadEngines() { + LOG("_syncLoadEngines: start"); // See if we have a cache file so we don't have to parse a bunch of XML. let cache = {}; let cacheEnabled = getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true); @@ -3164,26 +3222,37 @@ SearchService.prototype = { // nsIBrowserSearchService init: function SRCH_SVC_init(observer) { - if (gInitialized) { - if (observer) { - executeSoon(function () { - observer.onInitComplete(this._initRV); - }); - } - return; - } - - if (observer) - this._initObservers.push(observer); - + let self = this; if (!this._initStarted) { - executeSoon((function () { - // Someone may have since called syncInit via ensureInitialized - if so, - // nothing to do here. - if (!gInitialized) - this._syncInit(); - }).bind(this)); this._initStarted = true; + TaskUtils.spawn(function task() { + try { + yield engineMetadataService.init(); + if (gInitialized) { + // No need to pursue asynchronous initialization, + // synchronous fallback had to be called and has finished. + return; + } + // Complete initialization. In the current implementation, + // this is done by calling the synchronous initializer. + // Future versions might introduce an actually synchronous + // implementation. + self._syncInit(); + } catch (ex) { + self._initObservers.reject(ex); + } + }); + } + if (observer) { + TaskUtils.captureErrors(this._initObservers.promise.then( + function onSuccess() { + observer.onInitComplete(self._initRV); + }, + function onError(aReason) { + Components.utils.reportError("Internal error while initializing SearchService: " + aReason); + observer.onInitComplete(Components.results.NS_ERROR_UNEXPECTED); + } + )); } }, @@ -3568,48 +3637,183 @@ SearchService.prototype = { }; var engineMetadataService = { + _jsonFile: OS.Path.join(OS.Constants.Path.profileDir, "search-metadata.json"), + /** - * @type {nsIFile|null} The file holding the metadata. + * Possible values for |_initState|. + * + * We have two paths to perform initialization: a default asynchronous + * path and a fallback synchronous path that can interrupt the async + * path. For this reason, initialization is actually something of a + * finite state machine, represented with the following states: + * + * @enum */ - get _jsonFile() { - delete this._jsonFile; - return this._jsonFile = FileUtils.getFile(NS_APP_USER_PROFILE_50_DIR, - ["search-metadata.json"]); + _InitStates: { + NOT_STARTED: "NOT_STARTED" + /**Initialization has not started*/, + JSON_LOADING_ATTEMPTED: "JSON_LOADING_ATTEMPTED" + /**JSON file was loaded or does not exist*/, + FINISHED_SUCCESS: "FINISHED_SUCCESS" + /**Setup complete, with a success*/ }, /** - * Lazy getter for the file containing json data. + * The latest step completed by initialization. One of |InitStates| + * + * @type {engineMetadataService._InitStates} */ - get _store() { - delete this._store; - return this._store = this._loadStore(); + _initState: null, + + // A promise fulfilled once initialization is complete + _initializer: null, + + /** + * Asynchronous initializer + * + * Note: In the current implementation, initialization never fails. + */ + init: function epsInit() { + if (!this._initializer) { + // Launch asynchronous initialization + let initializer = this._initializer = Promise.defer(); + TaskUtils.spawn((function task_init() { + LOG("metadata init: starting"); + switch(this._initState) { + case engineMetadataService._InitStates.NOT_STARTED: + // 1. Load json file if it exists + try { + let contents = yield OS.File.read(this._jsonFile); + if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) { + // No need to pursue asynchronous initialization, + // synchronous fallback was called and has finished. + return; + } + this._store = JSON.parse(new TextDecoder().decode(contents)); + this._initState = engineMetadataService._InitStates.FINISHED_SUCCESS; + } catch (ex) { + if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) { + // No need to pursue asynchronous initialization, + // synchronous fallback was called and has finished. + return; + } + if (ex.becauseNoSuchFile) { + // If the file does not exist, we need to continue initialization + this._initState = engineMetadataService._InitStates.JSON_LOADING_ATTEMPTED; + } else { + // Otherwise, we are done + LOG("metadata init: could not load JSON file " + ex); + this._store = {}; + this._initState = engineMetadataService._InitStates.FINISHED_SUCCESS; + return; + } + } + // Fall through to the next state + + case engineMetadataService._InitStates.JSON_LOADING_ATTEMPTED: + // 2. Otherwise, load db + try { + let store = yield this._asyncMigrateOldDB(); + if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) { + // No need to pursue asynchronous initialization, + // synchronous fallback was called and has finished. + return; + } + if (!store) { + LOG("metadata init: No store to migrate to disk"); + this._store = {}; + } else { + // Commit the migrated store to disk immediately + LOG("metadata init: Committing the migrated store to disk"); + this._store = store; + this._commit(store); + } + } catch (ex) { + if (this._initState == engineMetadataService._InitStates.FINISHED_SUCCESS) { + // No need to pursue asynchronous initialization, + // synchronous fallback was called and has finished. + return; + } + LOG("metadata init: Error migrating store, using an empty store: " + ex); + this._store = {}; + } + this._initState = engineMetadataService._InitStates.FINISHED_SUCCESS; + break; + + default: + throw new Error("Internal error: invalid state " + this._initState); + }}).bind(this)).then( + + // 3. Inform any observers + function onSuccess() { + initializer.resolve(); + }, + function onError() { + initializer.reject(); + } + ); + } + return TaskUtils.captureErrors(this._initializer.promise); }, - // Perform loading the first time |_store| is accessed. - _loadStore: function() { - let jsonFile = this._jsonFile; - if (!jsonFile.exists()) { - LOG("loadStore: search-metadata.json does not exist"); + /** + * Synchronous implementation of initializer + * + * This initializer is able to pick wherever the async initializer + * is waiting. The asynchronous initializer is expected to stop + * if it detects that the synchronous initializer has completed + * initialization. + */ + syncInit: function epsSyncInit() { + LOG("metadata syncInit: starting"); + switch(this._initState) { + case engineMetadataService._InitStates.NOT_STARTED: + let jsonFile = new FileUtils.File(this._jsonFile); + // 1. Load json file if it exists + if (jsonFile.exists()) { + try { + let uri = Services.io.newFileURI(jsonFile); + let stream = Services.io.newChannelFromURI(uri).open(); + this._store = parseJsonFromStream(stream); + } catch (x) { + LOG("metadata syncInit: could not load JSON file " + x); + this._store = {}; + } + this._initState = this._InitStates.FINISHED_SUCCESS; + break; + } + this._initState = this._InitStates.JSON_LOADING_ATTEMPTED; + // Fall through to the next state - // First check to see whether there's an existing SQLite DB to migrate - let store = this._migrateOldDB(); - if (store) { - // Commit the migrated store to disk immediately - LOG("Committing the migrated store to disk"); - this._commit(store); - return store; - } + case engineMetadataService._InitStates.JSON_LOADING_ATTEMPTED: + // 2. No json, attempt to migrate from a database + try { + let store = this._syncMigrateOldDB(); + if (!store) { + LOG("metadata syncInit: No store to migrate to disk"); + this._store = {}; + } else { + // Commit the migrated store to disk immediately + LOG("metadata syncInit: Committing the migrated store to disk"); + this._store = store; + this._commit(store); + } + } catch (ex) { + LOG("metadata syncInit: Error migrating store, using an empty store: " + ex); + this._store = {}; + } + this._initState = engineMetadataService._InitStates.FINISHED_SUCCESS; + break; - // Migration failed, or this is a first-run - just use an empty store - return {}; + default: + throw new Error("Internal error: invalid state " + this._initState); } - LOG("loadStore: attempting to load store from JSON file"); - try { - return parseJsonFromStream(NetUtil.newChannel(jsonFile).open()); - } catch (x) { - LOG("loadStore failed to load file: "+x); - return {}; + // 3. Inform any observers + if (this._initializer) { + this._initializer.resolve(); + } else { + this._initializer = Promise.resolve(); } }, @@ -3689,44 +3893,86 @@ var engineMetadataService = { } }, - /** - * Migrate search.sqlite - * - * Notes: - * - we do not remove search.sqlite after migration, so as to allow - * downgrading and forensics; - */ - _migrateOldDB: function SRCH_SVC_EMS_migrate() { - LOG("SRCH_SVC_EMS_migrate start"); - let sqliteFile = FileUtils.getFile(NS_APP_USER_PROFILE_50_DIR, - ["search.sqlite"]); - if (!sqliteFile.exists()) { - LOG("SRCH_SVC_EMS_migrate search.sqlite does not exist"); - return null; - } - let store = {}; - try { - LOG("SRCH_SVC_EMS_migrate Migrating data from SQL"); - const sqliteDb = Services.storage.openDatabase(sqliteFile); - const statement = sqliteDb.createStatement("SELECT * from engine_data"); - while (statement.executeStep()) { - let row = statement.row; - let engine = row.engineid; - let name = row.name; - let value = row.value; - if (!store[engine]) { - store[engine] = {}; - } + _syncMigrateOldDB: function SRCH_SVC_EMS_migrate() { + LOG("SRCH_SVC_EMS_migrate start"); + let sqliteFile = FileUtils.getFile(NS_APP_USER_PROFILE_50_DIR, + ["search.sqlite"]); + if (!sqliteFile.exists()) { + LOG("SRCH_SVC_EMS_migrate search.sqlite does not exist"); + return null; + } + let store = {}; + try { + LOG("SRCH_SVC_EMS_migrate Migrating data from SQL"); + const sqliteDb = Services.storage.openDatabase(sqliteFile); + const statement = sqliteDb.createStatement("SELECT * from engine_data"); + while (statement.executeStep()) { + let row = statement.row; + let engine = row.engineid; + let name = row.name; + let value = row.value; + if (!store[engine]) { + store[engine] = {}; + } store[engine][name] = value; } statement.finalize(); sqliteDb.close(); - } catch (ex) { - LOG("SRCH_SVC_EMS_migrate failed: " + ex); - return null; - } - return store; - }, + } catch (ex) { + LOG("SRCH_SVC_EMS_migrate failed: " + ex); + return null; + } + return store; + }, + + /** + * Migrate search.sqlite, asynchronously + * + * Notes: + * - we do not remove search.sqlite after migration, so as to allow + * downgrading and forensics; + */ + _asyncMigrateOldDB: function SRCH_SVC_EMS_asyncMigrate() { + LOG("SRCH_SVC_EMS_asyncMigrate start"); + return TaskUtils.spawn(function task() { + let sqliteFile = FileUtils.getFile(NS_APP_USER_PROFILE_50_DIR, + ["search.sqlite"]); + if (!(yield OS.File.exists(sqliteFile.path))) { + LOG("SRCH_SVC_EMS_migrate search.sqlite does not exist"); + throw new Task.Result(); // Bail out + } + let store = {}; + LOG("SRCH_SVC_EMS_migrate Migrating data from SQL"); + const sqliteDb = Services.storage.openDatabase(sqliteFile); + const statement = sqliteDb.createStatement("SELECT * from engine_data"); + try { + yield TaskUtils.executeStatement( + statement, + function onResult(aResultSet) { + while (true) { + let row = aResultSet.getNextRow(); + if (!row) { + break; + } + let engine = row.engineid; + let name = row.name; + let value = row.value; + if (!store[engine]) { + store[engine] = {}; + } + store[engine][name] = value; + } + } + ); + } catch(ex) { + // If loading the db failed, ignore the db + throw new Task.Result(); // Bail out + } finally { + sqliteDb.asyncClose(); + } + throw new Task.Result(store); + }); + }, /** * Commit changes to disk, asynchronously. @@ -3741,7 +3987,6 @@ var engineMetadataService = { */ _commit: function epsCommit(aStore) { LOG("epsCommit: start"); - let store = aStore || this._store; if (!store) { LOG("epsCommit: nothing to do"); @@ -3750,39 +3995,32 @@ var engineMetadataService = { if (!this._lazyWriter) { LOG("epsCommit: initializing lazy writer"); - let jsonFile = this._jsonFile; function writeCommit() { LOG("epsWriteCommit: start"); - let ostream = FileUtils. - openSafeFileOutputStream(jsonFile, - MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE); - - // Obtain a converter to convert our data to a UTF-8 encoded input stream. - let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. - createInstance(Ci.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - - let callback = function(result) { - if (Components.isSuccessCode(result)) { - ostream.close(); + let data = gEncoder.encode(JSON.stringify(store)); + let path = engineMetadataService._jsonFile; + LOG("epsCommit path " + path); + let promise = OS.File.writeAtomic(path, data, { tmpPath: path + ".tmp" }); + promise = promise.then( + function onSuccess() { Services.obs.notifyObservers(null, - SEARCH_SERVICE_TOPIC, - SEARCH_SERVICE_METADATA_WRITTEN); + SEARCH_SERVICE_TOPIC, + SEARCH_SERVICE_METADATA_WRITTEN); + LOG("epsWriteCommit: done " + result); } - LOG("epsWriteCommit: done " + result); - }; - // Asynchronously copy the data to the file. - let istream = converter.convertToInputStream(JSON.stringify(store)); - NetUtil.asyncCopy(istream, ostream, callback); + ); + TaskUtils.captureErrors(promise); } this._lazyWriter = new DeferredTask(writeCommit, LAZY_SERIALIZE_DELAY); } LOG("epsCommit: (re)setting timer"); this._lazyWriter.start(); }, - _lazyWriter: null, + _lazyWriter: null }; +engineMetadataService._initState = engineMetadataService._InitStates.NOT_STARTED; + const SEARCH_UPDATE_LOG_PREFIX = "*** Search update: "; /** From f931ddad9f1dc3a4871de75cee21509ce80b982a Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Thu, 22 Nov 2012 12:09:57 +0100 Subject: [PATCH 119/217] Fix for bug 821606 (Turn on WebIDL bindings for Element and HTMLElement). r=bz. --HG-- extra : rebase_source : f85002d801871a3a99c1b8bc121509ae87d2f665 --- content/html/content/src/nsHTMLDivElement.cpp | 7 +++++++ content/html/content/src/nsHTMLDivElement.h | 8 ++++++-- content/html/content/src/nsHTMLElement.cpp | 11 +++++++++++ content/html/content/src/nsHTMLUnknownElement.cpp | 8 ++++++++ content/html/content/src/nsHTMLUnknownElement.h | 9 +++++++-- content/html/content/test/test_bug389797.html | 3 +-- content/xml/content/src/nsXMLElement.cpp | 7 +++++++ content/xml/content/src/nsXMLElement.h | 5 ++++- dom/bindings/Bindings.conf | 10 +++++----- .../tests/approved/test_interfaces.html.json | 11 ++++------- dom/tests/mochitest/bugs/test_protochains.html | 7 +++---- dom/webidl/HTMLElement.webidl | 3 +-- dom/webidl/WebIDL.mk | 1 + js/xpconnect/src/nsDOMQS.h | 13 +++++++++++-- 14 files changed, 76 insertions(+), 27 deletions(-) diff --git a/content/html/content/src/nsHTMLDivElement.cpp b/content/html/content/src/nsHTMLDivElement.cpp index 698b208456bc..b74d324b9599 100644 --- a/content/html/content/src/nsHTMLDivElement.cpp +++ b/content/html/content/src/nsHTMLDivElement.cpp @@ -9,6 +9,7 @@ #include "nsGenericHTMLElement.h" #include "nsStyleConsts.h" #include "nsMappedAttributes.h" +#include "mozilla/dom/HTMLDivElementBinding.h" using namespace mozilla; using namespace mozilla::dom; @@ -19,6 +20,7 @@ NS_IMPL_NS_NEW_HTML_ELEMENT(Div) nsHTMLDivElement::nsHTMLDivElement(already_AddRefed aNodeInfo) : nsGenericHTMLElement(aNodeInfo) { + SetIsDOMBinding(); } nsHTMLDivElement::~nsHTMLDivElement() @@ -40,6 +42,11 @@ NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLDivElement) NS_IMPL_ELEMENT_CLONE(nsHTMLDivElement) +JSObject* +nsHTMLDivElement::WrapNode(JSContext *aCx, JSObject *aScope, bool *aTriedToWrap) +{ + return dom::HTMLDivElementBinding::Wrap(aCx, aScope, this, aTriedToWrap); +} bool nsHTMLDivElement::ParseAttribute(int32_t aNamespaceID, diff --git a/content/html/content/src/nsHTMLDivElement.h b/content/html/content/src/nsHTMLDivElement.h index b08623284f0f..c9cbfe1d9d6d 100644 --- a/content/html/content/src/nsHTMLDivElement.h +++ b/content/html/content/src/nsHTMLDivElement.h @@ -8,8 +8,8 @@ #include "nsGenericHTMLElement.h" #include "nsIDOMHTMLDivElement.h" -class nsHTMLDivElement : public nsGenericHTMLElement, - public nsIDOMHTMLDivElement +class nsHTMLDivElement MOZ_FINAL : public nsGenericHTMLElement, + public nsIDOMHTMLDivElement { public: nsHTMLDivElement(already_AddRefed aNodeInfo); @@ -61,6 +61,10 @@ public: virtual nsXPCClassInfo* GetClassInfo(); virtual nsIDOMNode* AsDOMNode() { return this; } + +protected: + virtual JSObject* WrapNode(JSContext *aCx, JSObject *aScope, + bool *aTriedToWrap) MOZ_OVERRIDE; }; #endif /* nsHTMLDivElement_h___ */ diff --git a/content/html/content/src/nsHTMLElement.cpp b/content/html/content/src/nsHTMLElement.cpp index 544d686cb3e8..2c030c0ea489 100644 --- a/content/html/content/src/nsHTMLElement.cpp +++ b/content/html/content/src/nsHTMLElement.cpp @@ -7,6 +7,7 @@ #include "nsIDOMHTMLElement.h" #include "nsContentUtils.h" +#include "mozilla/dom/HTMLElementBinding.h" using namespace mozilla; using namespace mozilla::dom; @@ -38,6 +39,10 @@ public: virtual nsXPCClassInfo* GetClassInfo(); virtual nsIDOMNode* AsDOMNode() { return this; } + +protected: + virtual JSObject* WrapNode(JSContext *aCx, JSObject *aScope, + bool *aTriedToWrap) MOZ_OVERRIDE; }; // Here, we expand 'NS_IMPL_NS_NEW_HTML_ELEMENT()' by hand. @@ -52,6 +57,7 @@ NS_NewHTMLElement(already_AddRefed aNodeInfo, nsHTMLElement::nsHTMLElement(already_AddRefed aNodeInfo) : nsGenericHTMLElement(aNodeInfo) { + SetIsDOMBinding(); } nsHTMLElement::~nsHTMLElement() @@ -90,3 +96,8 @@ nsHTMLElement::GetInnerHTML(nsAString& aInnerHTML, ErrorResult& aError) nsGenericHTMLElement::GetInnerHTML(aInnerHTML, aError); } +JSObject* +nsHTMLElement::WrapNode(JSContext *aCx, JSObject *aScope, bool *aTriedToWrap) +{ + return dom::HTMLElementBinding::Wrap(aCx, aScope, this, aTriedToWrap); +} diff --git a/content/html/content/src/nsHTMLUnknownElement.cpp b/content/html/content/src/nsHTMLUnknownElement.cpp index 23f17c591355..6069bd1383a4 100644 --- a/content/html/content/src/nsHTMLUnknownElement.cpp +++ b/content/html/content/src/nsHTMLUnknownElement.cpp @@ -4,12 +4,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsHTMLUnknownElement.h" +#include "mozilla/dom/HTMLElementBinding.h" using namespace mozilla::dom; NS_IMPL_ADDREF_INHERITED(nsHTMLUnknownElement, Element) NS_IMPL_RELEASE_INHERITED(nsHTMLUnknownElement, Element) +JSObject* +nsHTMLUnknownElement::WrapNode(JSContext *aCx, JSObject *aScope, + bool *aTriedToWrap) +{ + return HTMLUnknownElementBinding::Wrap(aCx, aScope, this, aTriedToWrap); +} + NS_IMPL_NS_NEW_HTML_ELEMENT(Unknown) DOMCI_NODE_DATA(HTMLUnknownElement, nsHTMLUnknownElement) diff --git a/content/html/content/src/nsHTMLUnknownElement.h b/content/html/content/src/nsHTMLUnknownElement.h index 2b6d70c69123..c29a05169dfa 100644 --- a/content/html/content/src/nsHTMLUnknownElement.h +++ b/content/html/content/src/nsHTMLUnknownElement.h @@ -8,13 +8,14 @@ #include "nsGenericHTMLElement.h" #include "nsIDOMHTMLUnknownElement.h" -class nsHTMLUnknownElement : public nsGenericHTMLElement - , public nsIDOMHTMLUnknownElement +class nsHTMLUnknownElement MOZ_FINAL : public nsGenericHTMLElement + , public nsIDOMHTMLUnknownElement { public: nsHTMLUnknownElement(already_AddRefed aNodeInfo) : nsGenericHTMLElement(aNodeInfo) { + SetIsDOMBinding(); if (NodeInfo()->Equals(nsGkAtoms::bdi)) { SetHasDirAuto(); } @@ -37,6 +38,10 @@ public: virtual nsXPCClassInfo* GetClassInfo(); virtual nsIDOMNode* AsDOMNode() { return this; } + +protected: + virtual JSObject* WrapNode(JSContext *aCx, JSObject *aScope, + bool *aTriedToWrap) MOZ_OVERRIDE; }; #endif /* nsHTMLUnknownElement_h___ */ diff --git a/content/html/content/test/test_bug389797.html b/content/html/content/test/test_bug389797.html index 5743af82c06f..e4407e07514a 100644 --- a/content/html/content/test/test_bug389797.html +++ b/content/html/content/test/test_bug389797.html @@ -41,8 +41,7 @@ function HTML_TAG(aTagName, aImplClass) { // inherit from them do. interfacesNonClassinfo[aTagName] = [ "nsIDOMNode", - "nsIDOMElement", - "nsISupportsWeakReference" ]; + "nsIDOMElement" ]; var interfaceName = "nsIDOM" + getClassName(aTagName); if (interfaceName in SpecialPowers.Ci) { // no nsIDOMHTMLSpanElement diff --git a/content/xml/content/src/nsXMLElement.cpp b/content/xml/content/src/nsXMLElement.cpp index 8a26a6776cc0..59f69b2d1f1e 100644 --- a/content/xml/content/src/nsXMLElement.cpp +++ b/content/xml/content/src/nsXMLElement.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsXMLElement.h" +#include "mozilla/dom/ElementBinding.h" #include "nsContentUtils.h" // nsAutoScriptBlocker using namespace mozilla::dom; @@ -31,6 +32,12 @@ NS_ELEMENT_INTERFACE_MAP_END NS_IMPL_ADDREF_INHERITED(nsXMLElement, Element) NS_IMPL_RELEASE_INHERITED(nsXMLElement, Element) +JSObject* +nsXMLElement::WrapNode(JSContext *aCx, JSObject *aScope, bool *aTriedToWrap) +{ + return ElementBinding::Wrap(aCx, aScope, this, aTriedToWrap); +} + NS_IMPL_ELEMENT_CLONE(nsXMLElement) nsresult diff --git a/content/xml/content/src/nsXMLElement.h b/content/xml/content/src/nsXMLElement.h index dcde49d78f2f..59e5c316ca7e 100644 --- a/content/xml/content/src/nsXMLElement.h +++ b/content/xml/content/src/nsXMLElement.h @@ -16,6 +16,7 @@ public: nsXMLElement(already_AddRefed aNodeInfo) : mozilla::dom::Element(aNodeInfo) { + SetIsDOMBinding(); } // nsISupports @@ -51,7 +52,9 @@ public: // Element overrides virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo); - +protected: + virtual JSObject* WrapNode(JSContext *aCx, JSObject *aScope, + bool *aTriedToWrap) MOZ_OVERRIDE; }; #endif // nsXMLElement_h___ diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 4bb91c83bbd1..bbc5c6022f86 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -213,7 +213,6 @@ DOMInterfaces = { 'Element': { 'hasXPConnectImpls': True, - 'register': False, 'hasInstanceInterface': 'nsIDOMElement', 'resultNotAddRefed': [ 'classList', 'attributes', 'children', 'firstElementChild', @@ -277,9 +276,12 @@ DOMInterfaces = { 'resultNotAddRefed': [ 'item' ] }, +'HTMLDivElement': { + 'nativeType': 'nsHTMLDivElement' +}, + 'HTMLElement': { 'nativeType': 'nsGenericHTMLElement', - 'register': False, 'hasXPConnectImpls': True, 'hasInstanceInterface': 'nsIDOMHTMLElement', 'resultNotAddRefed': [ @@ -303,9 +305,7 @@ DOMInterfaces = { }, 'HTMLUnknownElement': { - 'nativeType': 'nsHTMLUnknownElement', - 'register': False, - 'hasXPConnectImpls': True + 'nativeType': 'nsHTMLUnknownElement' }, 'IID': [ diff --git a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json index 77ee3ca756dd..1df3b8f1d233 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json @@ -139,17 +139,14 @@ "EventTarget interface: calling addEventListener(DOMString,EventListener,boolean) on document.doctype with too few arguments must throw TypeError": true, "EventTarget interface: calling removeEventListener(DOMString,EventListener,boolean) on document.doctype with too few arguments must throw TypeError": true, "EventTarget interface: calling dispatchEvent(Event) on document.doctype with too few arguments must throw TypeError": true, - "Element interface: existence and properties of interface object": true, - "Element interface: existence and properties of interface prototype object": true, - "Element interface: existence and properties of interface prototype object's \"constructor\" property": true, + "Element interface: attribute namespaceURI": true, + "Element interface: attribute prefix": true, + "Element interface: attribute localName": true, + "Element interface: attribute attributes": true, "Element interface: attribute className": true, "Element interface: operation remove()": true, - "Stringification of element": "debug", "Element interface: element must inherit property \"className\" with the proper type (5)": true, "Element interface: element must inherit property \"remove\" with the proper type (25)": true, - "EventTarget interface: calling addEventListener(DOMString,EventListener,boolean) on element with too few arguments must throw TypeError": true, - "EventTarget interface: calling removeEventListener(DOMString,EventListener,boolean) on element with too few arguments must throw TypeError": true, - "EventTarget interface: calling dispatchEvent(Event) on element with too few arguments must throw TypeError": true, "Attr interface: existence and properties of interface object": true, "Attr interface: existence and properties of interface prototype object": true, "Attr interface: existence and properties of interface prototype object's \"constructor\" property": true, diff --git a/dom/tests/mochitest/bugs/test_protochains.html b/dom/tests/mochitest/bugs/test_protochains.html index 2df98b8577b4..0e6149126614 100644 --- a/dom/tests/mochitest/bugs/test_protochains.html +++ b/dom/tests/mochitest/bugs/test_protochains.html @@ -24,10 +24,9 @@ is(Object.getPrototypeOf(HTMLElement.prototype), Element.prototype, is(Object.getPrototypeOf(document.createElementNS(null, "x")), Element.prototype, "Must have correct proto chain for random element"); -// Todo because of HTMLUnknownElement -todo_is(Object.getPrototypeOf(document.createElement("noSuchElementName")), - HTMLElement.prototype, - "Must have correct proto chain for random HTML element"); +is(Object.getPrototypeOf(document.createElement("noSuchElementName")), + HTMLUnknownElement.prototype, + "Must have correct proto chain for random HTML element"); // And check that it's really working as it should function checkPropPresent(propName, objList, expected) diff --git a/dom/webidl/HTMLElement.webidl b/dom/webidl/HTMLElement.webidl index 5beb01b758db..16429dc4015a 100644 --- a/dom/webidl/HTMLElement.webidl +++ b/dom/webidl/HTMLElement.webidl @@ -201,7 +201,6 @@ interface HTMLElement : Element { // FIXME Bug 810677 Move className from HTMLElement to Element attribute DOMString className; - /* Commented out for now because our quickstub setup doesn't handle calling PreEnabled() on our interface, which is what sets up the .expose pref here [SetterThrows,Pref="dom.w3c_touch_events.expose"] attribute EventHandler ontouchstart; [SetterThrows,Pref="dom.w3c_touch_events.expose"] @@ -213,7 +212,7 @@ interface HTMLElement : Element { [SetterThrows,Pref="dom.w3c_touch_events.expose"] attribute EventHandler ontouchleave; [SetterThrows,Pref="dom.w3c_touch_events.expose"] - attribute EventHandler ontouchcancel;*/ + attribute EventHandler ontouchcancel; [SetterThrows] attribute EventHandler oncopy; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 5aa653c3994c..5ed6f4027983 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -44,6 +44,7 @@ webidl_files = \ Function.webidl \ GainNode.webidl \ HTMLCollection.webidl \ + HTMLDivElement.webidl \ HTMLElement.webidl \ HTMLOptionsCollection.webidl \ HTMLPropertiesCollection.webidl \ diff --git a/js/xpconnect/src/nsDOMQS.h b/js/xpconnect/src/nsDOMQS.h index b82b1bff533f..001ba0f3a763 100644 --- a/js/xpconnect/src/nsDOMQS.h +++ b/js/xpconnect/src/nsDOMQS.h @@ -17,6 +17,10 @@ #include "nsHTMLDocument.h" #include "nsICSSDeclaration.h" #include "nsSVGStylableElement.h" +#include "mozilla/dom/EventTargetBinding.h" +#include "mozilla/dom/NodeBinding.h" +#include "mozilla/dom/ElementBinding.h" +#include "mozilla/dom/HTMLElementBinding.h" template struct ProtoIDAndDepth @@ -27,17 +31,22 @@ struct ProtoIDAndDepth }; }; -#define NEW_BINDING(_native) \ +#define NEW_BINDING(_native, _id) \ template<> \ struct ProtoIDAndDepth<_native> \ { \ enum { \ - PrototypeID = mozilla::dom::PrototypeIDMap<_native>::PrototypeID, \ + PrototypeID = mozilla::dom::prototypes::id::_id, \ Depth = mozilla::dom::PrototypeTraits< \ static_cast(PrototypeID)>::Depth \ }; \ } +NEW_BINDING(mozilla::dom::EventTarget, EventTarget); +NEW_BINDING(nsINode, Node); +NEW_BINDING(mozilla::dom::Element, Element); +NEW_BINDING(nsGenericHTMLElement, HTMLElement); + #define DEFINE_UNWRAP_CAST(_interface, _base, _bit) \ template <> \ MOZ_ALWAYS_INLINE JSBool \ From 93319e24aca51aa9b1ac8f541f26db4d3e0fe53b Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Fri, 21 Dec 2012 00:53:00 -0800 Subject: [PATCH 120/217] Bug 823784 - B2G crash: nsHTMLMediaElement::UpdateAudioChannelPlayingState r=smaug Don't delete a smart pointer; it can result in a double-free. --- dom/audiochannel/AudioChannelService.cpp | 1 - dom/audiochannel/AudioChannelServiceChild.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 2a7c365c2495..b1d5932e61c6 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -59,7 +59,6 @@ AudioChannelService::Shutdown() } if (gAudioChannelService) { - delete gAudioChannelService; gAudioChannelService = nullptr; } } diff --git a/dom/audiochannel/AudioChannelServiceChild.cpp b/dom/audiochannel/AudioChannelServiceChild.cpp index 7da37463c3cd..836204ab66da 100644 --- a/dom/audiochannel/AudioChannelServiceChild.cpp +++ b/dom/audiochannel/AudioChannelServiceChild.cpp @@ -47,7 +47,6 @@ void AudioChannelServiceChild::Shutdown() { if (gAudioChannelServiceChild) { - delete gAudioChannelServiceChild; gAudioChannelServiceChild = nullptr; } } From 5b2cfc1dea5d12e66123acfda4bb8cd8eabbad8b Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 21 Dec 2012 18:23:32 +0000 Subject: [PATCH 121/217] Bug 808318 - "ASSERTION: Children must have same reference frame" with SVG display lists disabled for painting. --- layout/svg/crashtests/808318-1.svg | 2 ++ layout/svg/crashtests/crashtests.list | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 layout/svg/crashtests/808318-1.svg diff --git a/layout/svg/crashtests/808318-1.svg b/layout/svg/crashtests/808318-1.svg new file mode 100644 index 000000000000..48907225ccea --- /dev/null +++ b/layout/svg/crashtests/808318-1.svg @@ -0,0 +1,2 @@ + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 2658f348aae2..4a244a4116ea 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -146,3 +146,5 @@ load 788831-1.svg load 790072.svg load 791826-1.svg load 789390-1.html +load 808318-1.svg + From f6455eb9eb9c47bbf32852a2dcf090cbc6b01770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Fri, 21 Dec 2012 10:28:55 -0800 Subject: [PATCH 122/217] Bug 818848 - We should keep a small space on storage during download/install [r=ferjm] --- dom/apps/src/AppDownloadManager.jsm | 98 +++++++++++++++++++++++++++++ dom/apps/src/FreeSpaceWatcher.jsm | 83 ++++++++++++++++++++++++ dom/apps/src/Makefile.in | 2 + dom/apps/src/Webapps.jsm | 44 ++++++++----- 4 files changed, 212 insertions(+), 15 deletions(-) create mode 100644 dom/apps/src/AppDownloadManager.jsm create mode 100644 dom/apps/src/FreeSpaceWatcher.jsm diff --git a/dom/apps/src/AppDownloadManager.jsm b/dom/apps/src/AppDownloadManager.jsm new file mode 100644 index 000000000000..58619beb7c1f --- /dev/null +++ b/dom/apps/src/AppDownloadManager.jsm @@ -0,0 +1,98 @@ +/* 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/. */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/FreeSpaceWatcher.jsm"); + +this.EXPORTED_SYMBOLS = ["AppDownloadManager"]; + +function debug(aMsg) { + //dump("-*-*- AppDownloadManager.jsm : " + aMsg + "\n"); +} + +this.AppDownloadManager = { + // Minimum disk free space we want to keep, in bytes. + // Keep synchronized with Webapps.jsm + MIN_REMAINING_FREESPACE: 5 * 1024 * 1024, + + downloads: {}, + count: 0, + cancelFunc: null, + timer: null, + + /** + * Registers the function called when we need to cancel a download. + * The function will be called with a single parameter being the + * manifest URL. + */ + registerCancelFunction: function app_dlMgr_registerCancel(aFunction) { + this.cancelFunc = aFunction; + }, + + /** + * Adds a download to the list of current downloads. + * @param aManifestURL The manifest URL for the application being downloaded. + * @param aDownload An opaque object representing the download. + */ + add: function app_dlMgr_add(aManifestURL, aDownload) { + debug("Adding " + aManifestURL); + if (!(aManifestURL in this.downloads)) { + this.count++; + if (this.count == 1) { + this.timer = FreeSpaceWatcher.create(this.MIN_REMAINING_FREESPACE, + this._spaceWatcher.bind(this)); + } + } + this.downloads[aManifestURL] = aDownload; + }, + + /** + * Retrieves a download from the list of current downloads. + * @param aManifestURL The manifest URL for the application being retrieved. + * @return The opaque object representing the download. + */ + get: function app_dlMgr_get(aManifestURL) { + debug("Getting " + aManifestURL); + return this.downloads[aManifestURL]; + }, + + /** + * Removes a download of the list of current downloads. + * @param aManifestURL The manifest URL for the application being removed. + */ + remove: function app_dlMgr_remove(aManifestURL) { + debug("Removing " + aManifestURL); + if (aManifestURL in this.downloads) { + this.count--; + delete this.downloads[aManifestURL]; + if (this.count == 0) { + FreeSpaceWatcher.stop(this.timer); + } + } + }, + + /** + * Callback for the free space watcher. This will call cancel on downloads + * if needed. + */ + _spaceWatcher: function app_dlMgr_watcher(aStatus) { + debug("Disk space is now " + aStatus); + if (aStatus == "free") { + // Nothing to do. + return; + } + + // We cancel all downloads, because we don't know which ones we could + // keep running. We can improve that later if we have better heuristics, + // or when we'll support pause & resume we should just pause downloads. + for (let url in this.downloads) { + this.cancelFunc(url, "INSUFFICIENT_STORAGE"); + } + } +} diff --git a/dom/apps/src/FreeSpaceWatcher.jsm b/dom/apps/src/FreeSpaceWatcher.jsm new file mode 100644 index 000000000000..b9b3f98895ca --- /dev/null +++ b/dom/apps/src/FreeSpaceWatcher.jsm @@ -0,0 +1,83 @@ +/* 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/. */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); + +this.EXPORTED_SYMBOLS = ["FreeSpaceWatcher"]; + +function debug(aMsg) { + //dump("-*-*- FreeSpaceWatcher.jsm : " + aMsg + "\n"); +} + +// Polling delay for free space, in ms. +const DEFAULT_WATCHER_DELAY = 1000; + +this.FreeSpaceWatcher = { + timers: {}, + id: 0, + + /** + * This function will call aOnStatusChange + * each time the free space for apps crosses aThreshold, checking + * every aDelay milliseconds, or every second by default. + * aOnStatusChange is called with either "free" or "full" and will + * always be called at least one to get the initial status. + * @param aThreshold The amount of space in bytes to watch for. + * @param aOnStatusChange The function called when the state changes. + * @param aDelay How often (in ms) we check free space. Defaults + * to DEFAULT_WATCHER_DELAY. + * @return An opaque value to use with stop(). + */ + create: function spaceWatcher_create(aThreshold, aOnStatusChange, aDelay) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + debug("Creating new FreeSpaceWatcher"); + let callback = { + currentStatus: null, + notify: function(aTimer) { + try { + let deviceStorage = Services.wm.getMostRecentWindow("navigator:browser") + .navigator.getDeviceStorage("apps"); + let req = deviceStorage.stat(); + req.onsuccess = req.onerror = function statResult(e) { + if (!e.target.result) { + return; + } + + let freeBytes = e.target.result.freeBytes; + debug("Free bytes: " + freeBytes); + let newStatus = freeBytes > aThreshold; + if (newStatus != callback.currentStatus) { + debug("New status: " + (newStatus ? "free" : "full")); + aOnStatusChange(newStatus ? "free" : "full"); + callback.currentStatus = newStatus; + } + } + } catch(e) { debug(e); } + } + } + + timer.initWithCallback(callback, aDelay || DEFAULT_WATCHER_DELAY, + Ci.nsITimer.TYPE_REPEATING_SLACK); + let id = "timer-" + this.id++; + this.timers[id] = timer; + return id; + }, + + /** + * This function stops a running watcher. + * @param aId The opaque timer id returned by create(). + */ + stop: function spaceWatcher_stop(aId) { + if (this.timers[aId]) { + this.timers[aId].cancel(); + delete this.timers[aId]; + } + } +} diff --git a/dom/apps/src/Makefile.in b/dom/apps/src/Makefile.in index fcbf34d24137..ce88eedeefc9 100644 --- a/dom/apps/src/Makefile.in +++ b/dom/apps/src/Makefile.in @@ -29,6 +29,8 @@ EXTRA_JS_MODULES += \ OfflineCacheInstaller.jsm \ PermissionsInstaller.jsm \ PermissionsTable.jsm \ + FreeSpaceWatcher.jsm \ + AppDownloadManager.jsm \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 3d929f98ffbc..74b42413bb46 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -19,6 +19,7 @@ Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/PermissionsInstaller.jsm"); Cu.import("resource://gre/modules/OfflineCacheInstaller.jsm"); Cu.import("resource://gre/modules/SystemMessagePermissionsChecker.jsm"); +Cu.import("resource://gre/modules/AppDownloadManager.jsm"); function debug(aMsg) { //dump("-*-*- Webapps.jsm : " + aMsg + "\n"); @@ -68,7 +69,6 @@ this.DOMApplicationRegistry = { webapps: { }, children: [ ], allAppsLaunchable: false, - downloads: { }, init: function() { this.messages = ["Webapps:Install", "Webapps:Uninstall", @@ -92,6 +92,8 @@ this.DOMApplicationRegistry = { Services.obs.addObserver(this, "xpcom-shutdown", false); + AppDownloadManager.registerCancelFunction(this.cancelDownload.bind(this)); + this.appsFile = FileUtils.getFile(DIRECTORY_NAME, ["webapps", "webapps.json"], true); @@ -867,9 +869,10 @@ this.DOMApplicationRegistry = { Services.obs.notifyObservers(aMm, "webapps-launch", JSON.stringify(aData)); }, - cancelDownload: function cancelDownload(aManifestURL) { + cancelDownload: function cancelDownload(aManifestURL, aError) { debug("cancelDownload " + aManifestURL); - let download = this.downloads[aManifestURL]; + let error = aError || "DOWNLOAD_CANCELED"; + let download = AppDownloadManager.get(aManifestURL); if (!download) { debug("Could not find a download for " + aManifestURL); return; @@ -898,8 +901,9 @@ this.DOMApplicationRegistry = { { type: "canceled", manifestURL: app.manifestURL, app: app, - error: "DOWNLOAD_CANCELED" }); + error: error }); }).bind(this)); + AppDownloadManager.remove(aManifestURL); }, startDownload: function startDownload(aManifestURL) { @@ -1080,7 +1084,7 @@ this.DOMApplicationRegistry = { appId: this._appIdForManifestURL(aApp.manifestURL), previousState: aIsUpdate ? "installed" : "pending" }; - this.downloads[aApp.manifestURL] = download; + AppDownloadManager.add(aApp.manifestURL, download); cacheUpdate.addObserver(new AppcacheObserver(aApp), false); if (aOfflineCacheObserver) { @@ -1163,7 +1167,7 @@ this.DOMApplicationRegistry = { app.installState = "installed"; app.downloading = false; - app.downloadsize = 0; + app.downloadSize = 0; app.readyToApplyDownload = false; app.downloadAvailable = !!manifest.appcache_path; @@ -1727,7 +1731,7 @@ this.DOMApplicationRegistry = { return; } - let download = self.downloads[aApp.manifestURL]; + let download = AppDownloadManager.get(aApp.manifestURL); app.downloading = false; // If there were not enough storage to download the packaged app we // won't have a record of the download details, so we just set the @@ -1738,6 +1742,7 @@ this.DOMApplicationRegistry = { manifestURL: aApp.manifestURL, error: aError, app: app }); + AppDownloadManager.remove(aApp.manifestURL); } function download() { @@ -1745,11 +1750,13 @@ this.DOMApplicationRegistry = { let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath()) .QueryInterface(Ci.nsIHttpChannel); - self.downloads[aApp.manifestURL] = { - channel: requestChannel, - appId: id, - previousState: aIsUpdate ? "installed" : "pending" - }; + AppDownloadManager.add(aApp.manifestURL, + { + channel: requestChannel, + appId: id, + previousState: aIsUpdate ? "installed" : "pending" + } + ); let lastProgressTime = 0; requestChannel.notificationCallbacks = { @@ -1821,6 +1828,11 @@ this.DOMApplicationRegistry = { bufferedOutputStream.close(); outputStream.close(); + if (!Components.isSuccessCode(aStatusCode)) { + cleanup("NETWORK_ERROR"); + return; + } + let certdb; try { certdb = Cc["@mozilla.org/security/x509certdb;1"] @@ -1886,7 +1898,6 @@ this.DOMApplicationRegistry = { if (aOnSuccess) { aOnSuccess(id, manifest); } - delete self.downloads[aApp.manifestURL]; } catch (e) { // Something bad happened when reading the package. if (typeof e == 'object') { @@ -1896,7 +1907,9 @@ this.DOMApplicationRegistry = { cleanup(e); } } finally { - zipReader.close(); + AppDownloadManager.remove(aApp.manifestURL); + if (zipReader) + zipReader.close(); } }); } @@ -1920,7 +1933,8 @@ this.DOMApplicationRegistry = { if (freeBytes) { debug("Free storage: " + freeBytes + ". Download size: " + aApp.downloadSize); - if (freeBytes <= aApp.downloadSize) { + if (freeBytes <= + aApp.downloadSize + AppDownloadManager.MIN_REMAINING_FREESPACE) { cleanup("INSUFFICIENT_STORAGE"); return; } From a7a7ab4ca9791c04e9086503a362164b1d76a7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Fri, 21 Dec 2012 10:28:58 -0800 Subject: [PATCH 123/217] Bug 820630 - Allow preinstalled packaged apps to specify the etag of the update manifest and package file [r=ferjm] --- dom/apps/src/AppsUtils.jsm | 1 + dom/apps/src/Webapps.jsm | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/dom/apps/src/AppsUtils.jsm b/dom/apps/src/AppsUtils.jsm index f06f2dce4557..5530820a782b 100644 --- a/dom/apps/src/AppsUtils.jsm +++ b/dom/apps/src/AppsUtils.jsm @@ -48,6 +48,7 @@ this.AppsUtils = { lastUpdateCheck: aApp.lastUpdateCheck, updateTime: aApp.updateTime, etag: aApp.etag, + packageEtag: aApp.packageEtag, installerAppId: aApp.installerAppId || Ci.nsIScriptSecurityManager.NO_APP_ID, installerIsBrowser: !!aApp.installerIsBrowser }; diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index 74b42413bb46..127d4152d326 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -1742,6 +1742,7 @@ this.DOMApplicationRegistry = { manifestURL: aApp.manifestURL, error: aError, app: app }); + self._saveApps(); AppDownloadManager.remove(aApp.manifestURL); } @@ -1750,6 +1751,10 @@ this.DOMApplicationRegistry = { let requestChannel = NetUtil.newChannel(aManifest.fullPackagePath()) .QueryInterface(Ci.nsIHttpChannel); + if (app.packageEtag) { + requestChannel.setRequestHeader("If-None-Match", app.packageEtag); + } + AppDownloadManager.add(aApp.manifestURL, { channel: requestChannel, @@ -1828,6 +1833,31 @@ this.DOMApplicationRegistry = { bufferedOutputStream.close(); outputStream.close(); + if (requestChannel.responseStatus == 304) { + // The package's Etag has not changed. + // We send a "applied" event right away. + app.downloading = false; + app.downloadAvailable = false; + app.downloadSize = 0; + app.installState = "installed"; + app.readyToApplyDownload = false; + self.broadcastMessage("Webapps:PackageEvent", { + type: "applied", + manifestURL: aApp.manifestURL, + app: app }); + // Save the updated registry, and cleanup the tmp directory. + self._saveApps(); + let file = FileUtils.getFile("TmpD", ["webapps", id], false); + if (file && file.exists()) { + file.remove(true); + } + return; + } + + // Save the new Etag for the package. + app.packageEtag = requestChannel.getResponseHeader("Etag"); + debug("Package etag=" + app.packageEtag); + if (!Components.isSuccessCode(aStatusCode)) { cleanup("NETWORK_ERROR"); return; From 690f70ba61cbe68fb961b4d96fdf186d2b704978 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Fri, 21 Dec 2012 10:29:49 -0800 Subject: [PATCH 124/217] Bug 821812, fix Android WebRTC signalling code hang by ensuring writable tmpdir for domain sockets, r=jesup --- .../sipcc/core/sipstack/sip_platform_task.c | 70 ++++++++++++++++--- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/media/webrtc/signaling/src/sipcc/core/sipstack/sip_platform_task.c b/media/webrtc/signaling/src/sipcc/core/sipstack/sip_platform_task.c index e02845ea27bc..d639f1d8e542 100644 --- a/media/webrtc/signaling/src/sipcc/core/sipstack/sip_platform_task.c +++ b/media/webrtc/signaling/src/sipcc/core/sipstack/sip_platform_task.c @@ -39,12 +39,16 @@ /* SIP Message queue waiting thread and the main thread IPC names */ #ifdef __ANDROID__ -#define SIP_IPC_TEMP_PATH "/data/data/com.cisco.telephony.provider/SIP-%d" +/* Note that for unit tests to work, this directory currently _must_ be + world-writable or the tests must be run as root. See bug 823741 for the + gory details. */ +#define SIP_IPC_TEMP_BASEPATH "/data/local/tmp" #else -#define SIP_IPC_TEMP_PATH "/tmp/SIP-%d" +#define SIP_IPC_TEMP_BASEPATH "/tmp" #endif -#define SIP_MSG_SERV_NAME "Main" -#define SIP_MSG_CLNT_NAME "MsgQ" +#define SIP_IPC_TEMP_DIRNAME "SIP-%d" +#define SIP_MSG_SERV_SUFFIX "/Main" +#define SIP_MSG_CLNT_SUFFIX "/MsgQ" #define SIP_PAUSE_WAIT_IPC_LISTEN_READY_TIME 50 /* 50ms. */ #define SIP_MAX_WAIT_FOR_IPC_LISTEN_READY 1200 /* 50 * 1200 = 1 minutes */ @@ -131,6 +135,42 @@ sip_platform_task_init (void) return; } +/** + * sip_get_sock_dir_tmpl creates a template for the name of a directory + * where IPC sockets will live. If the TMPDIR environment is set, that is used + * as a base; otherwise SIP_IPC_TEMP_BASEPATH is used as a fallback. + * SIP_IPC_TEMP_DIRNAME is added as a child directory, and if suffix is non-null, + * that is appended to the end. + * + * The primary motivation for using TMPDIR is that that is how Fennec + * (GeckoAppShell.java) passes in a scratch directory that is guaranteed to be + * writable on Android, and there's no other reliable way to get such a thing. + * + * @param[out] out buffer to be written to + * @param[in] outlen length of out buffer so we don't overrun + * @param[in] suffix if non-NULL, appended to the template + * + * @return The length of the written output not including the NULL + * terminator, or -1 if an error occurs. + */ +static PRUint32 sip_get_sock_dir_tmpl(char *out, PRUint32 outlen, + const char *suffix) { + + char *tmpdir; + tmpdir = getenv("TMPDIR"); + + if (suffix) { + return PR_snprintf(out, outlen, "%s/%s%s", + tmpdir ? tmpdir : SIP_IPC_TEMP_BASEPATH, + SIP_IPC_TEMP_DIRNAME, + suffix); + } + + return PR_snprintf(out, outlen, "%s/%s", + tmpdir ? tmpdir : SIP_IPC_TEMP_BASEPATH, + SIP_IPC_TEMP_DIRNAME); +} + /** * sip_create_IPC_sock creates and bind the socket for IPC. * @@ -208,6 +248,7 @@ void sip_platform_task_msgqwait (void *arg) uint8_t num_messages = 0; uint8_t response = 0; boolean quit_thread = FALSE; + char template[sizeof(sip_serv_sock_addr.sun_path)]; if (msgq == NULL) { CCSIP_DEBUG_ERROR(SIP_F_PREFIX"task msgq is null, exiting\n", fname); @@ -244,8 +285,8 @@ void sip_platform_task_msgqwait (void *arg) * The main thread is ready. set global client socket address * so that the server can send back response. */ - cpr_set_sockun_addr(&sip_clnt_sock_addr, - SIP_IPC_TEMP_PATH "/" SIP_MSG_CLNT_NAME, getpid()); + sip_get_sock_dir_tmpl(template, sizeof(template), SIP_MSG_CLNT_SUFFIX); + cpr_set_sockun_addr(&sip_clnt_sock_addr, template, getpid()); sip_ipc_clnt_socket = sip_create_IPC_sock(sip_clnt_sock_addr.sun_path); @@ -367,6 +408,9 @@ static void sip_process_int_msg (void) syshdr = int_msg->syshdr; if (msg != NULL && syshdr != NULL) { if (syshdr->Cmd == THREAD_UNLOAD) { + char template[sizeof(sip_serv_sock_addr.sun_path)]; + char stmpdir[sizeof(sip_serv_sock_addr.sun_path)]; + /* * Cleanup here, as SIPTaskProcessListEvent wont return. * - Remove last tmp file and tmp dir. @@ -374,8 +418,8 @@ static void sip_process_int_msg (void) cprCloseSocket(sip_ipc_serv_socket); unlink(sip_serv_sock_addr.sun_path); - char stmpdir[sizeof(sip_serv_sock_addr.sun_path)]; - PR_snprintf(stmpdir, sizeof(stmpdir), SIP_IPC_TEMP_PATH, getpid()); + sip_get_sock_dir_tmpl(template, sizeof(template), NULL); + PR_snprintf(stmpdir, sizeof(stmpdir), template, getpid()); if (rmdir(stmpdir) != 0) { CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to remove temp dir\n", fname); @@ -452,16 +496,20 @@ sip_platform_task_loop (void *arg) * Setup IPC socket addresses for main thread (server) */ { + char template[sizeof(sip_serv_sock_addr.sun_path)]; char stmpdir[sizeof(sip_serv_sock_addr.sun_path)]; - PR_snprintf(stmpdir, sizeof(stmpdir), SIP_IPC_TEMP_PATH, getpid()); + + sip_get_sock_dir_tmpl(template, sizeof(template), NULL); + PR_snprintf(stmpdir, sizeof(stmpdir), template, getpid()); if (mkdir(stmpdir, 0700) != 0) { CCSIP_DEBUG_ERROR(SIP_F_PREFIX"failed to create temp dir\n", fname); return; } + + sip_get_sock_dir_tmpl(template, sizeof(template), SIP_MSG_SERV_SUFFIX); + cpr_set_sockun_addr(&sip_serv_sock_addr, template, getpid()); } - cpr_set_sockun_addr(&sip_serv_sock_addr, - SIP_IPC_TEMP_PATH "/" SIP_MSG_SERV_NAME, getpid()); /* * Create IPC between the message queue thread and this main From 156aca9f596c3083a5b93f2b6ee2e86adb4db454 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 20 Dec 2012 17:57:29 -0500 Subject: [PATCH 125/217] Make SimpleTest.js runnable in non-Gecko browsers again. No bug, r=jmaher over IRC --HG-- rename : browser/themes/gnomestripe/downloads/allDownloadsViewOverlay.css => browser/themes/gnomestripe/downloads/downloads.css rename : browser/themes/winstripe/downloads/allDownloadsViewOverlay.css => browser/themes/winstripe/downloads/downloads.css rename : toolkit/components/places/tests/mochitest/test_bug_461710_perwindowpb.html => toolkit/components/places/tests/mochitest/test_bug_461710.html extra : rebase_source : a0c2241ee37c74d1df60cccc02b3b08fb7c68f58 --- testing/mochitest/tests/SimpleTest/SimpleTest.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 1215586d9288..933173c114c0 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -286,8 +286,10 @@ SimpleTest._logResult = function(test, passString, failString) { } else { parentRunner.log(msg); } - } else { + } else if (typeof dump === "function") { dump(msg + "\n"); + } else { + // Non-Mozilla browser? Just do nothing. } }; @@ -600,7 +602,7 @@ SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn, // Build a default validator function for common string input. var inputValidatorFn = typeof(aExpectedStringOrValidatorFn) == "string" - ? function(aData) aData == aExpectedStringOrValidatorFn + ? function(aData) { return aData == aExpectedStringOrValidatorFn; } : aExpectedStringOrValidatorFn; // reset for the next use @@ -628,7 +630,7 @@ SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn, reset(); successFn(); } else { - setTimeout(function() wait(validatorFn, successFn, failureFn, flavor), 100); + setTimeout(function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100); } } @@ -636,7 +638,7 @@ SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn, var preExpectedVal = SimpleTest._waitForClipboardMonotonicCounter + "-waitForClipboard-known-value"; SpecialPowers.clipboardCopyString(preExpectedVal); - wait(function(aData) aData == preExpectedVal, + wait(function(aData) { return aData == preExpectedVal; }, function() { // Call the original setup fn aSetupFn(); From 54d1925f8b942d8bc9fcfa686298320cae807046 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sat, 15 Sep 2012 11:19:55 -0700 Subject: [PATCH 126/217] Bug 823283 - Don't query for JSRESOLVE_QUALIFIED when resolving document.all. r=bz --HG-- extra : rebase_source : 3e3661f1fc6539ed885c91eed12bc9636d266f28 --- dom/base/nsDOMClassInfo.cpp | 11 +++--- dom/base/test/Makefile.in | 1 + .../test/test_document.all_unqualified.html | 35 +++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 dom/base/test/test_document.all_unqualified.html diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 3b0c9f36dff7..578b46015fb1 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -5220,9 +5220,7 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj, JSMutableHandleObject objp) { if ((flags & JSRESOLVE_ASSIGNING) || !JSID_IS_STRING(id)) { - // Nothing to do here if we're assigning or resolving a non-string - // property. - + // Nothing to do if we're assigning or resolving a non-string property. return JS_TRUE; } @@ -9027,10 +9025,9 @@ nsHTMLDocumentSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, ::JS_SetPrototype(cx, tmp, proto); } - // If we don't already have a helper, and we're resolving - // document.all qualified, and "all" isn't already defined - // on our prototype, create a helper. - if (!helper && (flags & JSRESOLVE_QUALIFIED) && !hasAll) { + // If we don't already have a helper and "all" isn't already defined on + // our prototype, create a helper. + if (!helper && !hasAll) { // Print a warning so developers can stop using document.all PrintWarningOnConsole(cx, "DocumentAllUsed"); diff --git a/dom/base/test/Makefile.in b/dom/base/test/Makefile.in index a3931ca30fb7..b2343a5a51e0 100644 --- a/dom/base/test/Makefile.in +++ b/dom/base/test/Makefile.in @@ -11,6 +11,7 @@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES = \ + test_document.all_unqualified.html \ test_domrequest.html \ test_gsp-standards.html \ test_gsp-quirks.html \ diff --git a/dom/base/test/test_document.all_unqualified.html b/dom/base/test/test_document.all_unqualified.html new file mode 100644 index 000000000000..763ba9898f7c --- /dev/null +++ b/dom/base/test/test_document.all_unqualified.html @@ -0,0 +1,35 @@ + + + + Test for Bug 823283 + + + + +Mozilla Bug 823283 +

    + +
    +
    +
    + + From 3ec6b10f96a9070090c84df2df937aeee484ba67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Fri, 21 Dec 2012 20:04:36 +0100 Subject: [PATCH 127/217] Bug 823292 - Stop setting the obsolete browsingmode attribute. r=ehsan --- browser/base/content/browser.xul | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 6cc2272042f9..265ecf3520d4 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -44,7 +44,6 @@ windowtype="navigator:browser" macanimationtype="document" screenX="4" screenY="4" - browsingmode="normal" fullscreenbutton="true" persist="screenX screenY width height sizemode"> From 587bd537196009b5bc24eb449bc6d42995c90790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Fri, 21 Dec 2012 20:09:07 +0100 Subject: [PATCH 128/217] Bug 823443 - Provide an easy way to add custom content to popup notifications. r=gavin --- toolkit/content/PopupNotifications.jsm | 60 ++++++++++++++++++++---- toolkit/content/widgets/notification.xml | 1 + 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/toolkit/content/PopupNotifications.jsm b/toolkit/content/PopupNotifications.jsm index 68ed9861c118..67eee03d40e3 100644 --- a/toolkit/content/PopupNotifications.jsm +++ b/toolkit/content/PopupNotifications.jsm @@ -16,6 +16,7 @@ const ICON_SELECTOR = ".notification-anchor-icon"; const ICON_ATTRIBUTE_SHOWING = "showing"; let popupNotificationsMap = new WeakMap(); +let gNotificationParents = new WeakMap; /** * Notification object describes a single popup notification. @@ -395,21 +396,61 @@ PopupNotifications.prototype = { }, /** - * + * Removes all notifications from the notification popup. */ + _clearPanel: function () { + let popupnotification; + while ((popupnotification = this.panel.lastChild)) { + this.panel.removeChild(popupnotification); + + // If this notification was provided by the chrome document rather than + // created ad hoc, move it back to where we got it from. + let originalParent = gNotificationParents.get(popupnotification); + if (originalParent) { + popupnotification.notification = null; + + // Remove nodes dynamically added to the notification's menu button + // in _refreshPanel. Keep popupnotificationcontent nodes; they are + // provided by the chrome document. + let contentNode = popupnotification.lastChild; + while (contentNode) { + let previousSibling = contentNode.previousSibling; + if (contentNode.nodeName != "popupnotificationcontent") + popupnotification.removeChild(contentNode); + contentNode = previousSibling; + } + + // Re-hide the notification such that it isn't rendered in the chrome + // document. _refreshPanel will unhide it again when needed. + popupnotification.hidden = true; + + originalParent.appendChild(popupnotification); + } + } + }, + _refreshPanel: function PopupNotifications_refreshPanel(notificationsToShow) { - while (this.panel.lastChild) - this.panel.removeChild(this.panel.lastChild); + this._clearPanel(); const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; notificationsToShow.forEach(function (n) { let doc = this.window.document; - let popupnotification = doc.createElementNS(XUL_NS, "popupnotification"); - popupnotification.setAttribute("label", n.message); + // Append "-notification" to the ID to try to avoid ID conflicts with other stuff // in the document. - popupnotification.setAttribute("id", n.id + "-notification"); + let popupnotificationID = n.id + "-notification"; + + // If the chrome document provides a popupnotification with this id, use + // that. Otherwise create it ad-hoc. + let popupnotification = doc.getElementById(popupnotificationID); + if (popupnotification) + gNotificationParents.set(popupnotification, popupnotification.parentNode); + else + popupnotification = doc.createElementNS(XUL_NS, "popupnotification"); + + popupnotification.setAttribute("label", n.message); + popupnotification.setAttribute("id", popupnotificationID); popupnotification.setAttribute("popupid", n.id); popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._dismiss();"); if (n.mainAction) { @@ -441,6 +482,10 @@ PopupNotifications.prototype = { } this.panel.appendChild(popupnotification); + + // The popupnotification may be hidden if we got it from the chrome + // document rather than creating it ad hoc. + popupnotification.hidden = false; }, this); }, @@ -622,8 +667,7 @@ PopupNotifications.prototype = { } }, this); - while (this.panel.lastChild) - this.panel.removeChild(this.panel.lastChild); + this._clearPanel(); this._update(); }, diff --git a/toolkit/content/widgets/notification.xml b/toolkit/content/widgets/notification.xml index 2df6e6eedd5d..b66bafb4b6de 100644 --- a/toolkit/content/widgets/notification.xml +++ b/toolkit/content/widgets/notification.xml @@ -449,6 +449,7 @@ + From 935330a0a28f0c2a366a776eda5cfd9e3d03fca4 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 21 Dec 2012 19:13:48 +0000 Subject: [PATCH 129/217] Bug 767535 - "ASSERTION: How did we getting here, then?" --- layout/svg/crashtests/767535-1.xhtml | 22 ++++++++++++++++++++++ layout/svg/crashtests/crashtests.list | 1 + 2 files changed, 23 insertions(+) create mode 100644 layout/svg/crashtests/767535-1.xhtml diff --git a/layout/svg/crashtests/767535-1.xhtml b/layout/svg/crashtests/767535-1.xhtml new file mode 100644 index 000000000000..d92bd3b1a339 --- /dev/null +++ b/layout/svg/crashtests/767535-1.xhtml @@ -0,0 +1,22 @@ + + + + + +
    +
    + + + + + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 4a244a4116ea..82603acb04e7 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -136,6 +136,7 @@ load 767056-1.svg load 768351.svg load 780963-1.html load 757751-1.svg +load 767535-1.xhtml load 768087-1.html load 778492-1.svg load 779971-1.svg From ecec5b899d4f3aa851d14792a12724bab378a47f Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Fri, 21 Dec 2012 11:14:51 -0800 Subject: [PATCH 130/217] Bug 823411 - Permission Prompt Helper doesn't forward access field to prompt. r=sicking --- dom/permission/PermissionPromptHelper.jsm | 1 + dom/permission/PermissionPromptService.js | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dom/permission/PermissionPromptHelper.jsm b/dom/permission/PermissionPromptHelper.jsm index f06fd7e396f0..70855e51dc29 100644 --- a/dom/permission/PermissionPromptHelper.jsm +++ b/dom/permission/PermissionPromptHelper.jsm @@ -94,6 +94,7 @@ this.PermissionPromptHelper = { // create a nsIContentPermissionRequest let request = { type: msg.type, + access: msg.access ? msg.access : "unused", principal: principal, QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]), allow: aCallbacks.allow, diff --git a/dom/permission/PermissionPromptService.js b/dom/permission/PermissionPromptService.js index ef6a78807d9f..954fd2c94fc4 100644 --- a/dom/permission/PermissionPromptService.js +++ b/dom/permission/PermissionPromptService.js @@ -68,9 +68,11 @@ PermissionPromptService.prototype = { + "2nd argument must be type 'nsIContentPermissionRequest'"); } + let type = aRequest.access !== "unused" ? aRequest.type + "-" + aRequest.access + : aRequest.type; let perm = - permissionManager.testExactPermissionFromPrincipal(aRequest.principal, - aRequest.type); + permissionManager.testExactPermissionFromPrincipal(aRequest.principal, type); + switch (perm) { case Ci.nsIPermissionManager.ALLOW_ACTION: aRequest.allow(); From 25f417dab52094fee2abf0e7af326b45ef824252 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Fri, 21 Dec 2012 14:29:16 -0500 Subject: [PATCH 131/217] Bug 822258 - Add winstripe theming for the new Downloads view in Places. r=mak. --- .../allDownloadsViewOverlay-aero.css | 35 +++ .../downloads/allDownloadsViewOverlay.css | 284 +----------------- browser/themes/winstripe/jar.mn | 2 +- 3 files changed, 51 insertions(+), 270 deletions(-) create mode 100644 browser/themes/winstripe/downloads/allDownloadsViewOverlay-aero.css diff --git a/browser/themes/winstripe/downloads/allDownloadsViewOverlay-aero.css b/browser/themes/winstripe/downloads/allDownloadsViewOverlay-aero.css new file mode 100644 index 000000000000..188f00931b27 --- /dev/null +++ b/browser/themes/winstripe/downloads/allDownloadsViewOverlay-aero.css @@ -0,0 +1,35 @@ +/* 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/. */ + +%define WINSTRIPE_AERO +%include allDownloadsViewOverlay.css +%undef WINSTRIPE_AERO + +@media (-moz-windows-default-theme) { + /* + -moz-appearance: menuitem is almost right, but the hover effect is not + transparent and is lighter than desired. + + Copied from the autocomplete richlistbox styling in + toolkit/themes/winstripe/global/autocomplete.css + + This styling should be kept in sync with the style from the above file. + */ + #downloadsRichListBox > richlistitem.download[selected] { + color: inherit; + background-color: transparent; + /* four gradients for the bevel highlights on each edge, one for blue background */ + background-image: + -moz-linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, rgba(255,255,255,0) 3px), + -moz-linear-gradient(to right, rgba(255,255,255,0.5) 3px, rgba(255,255,255,0) 3px), + -moz-linear-gradient(to left, rgba(255,255,255,0.5) 3px, rgba(255,255,255,0) 3px), + -moz-linear-gradient(to top, rgba(255,255,255,0.4) 3px, rgba(255,255,255,0) 3px), + -moz-linear-gradient(to bottom, rgba(163,196,247,0.3), rgba(122,180,246,0.3)); + background-clip: content-box; + border-radius: 6px; + outline: 1px solid rgb(124,163,206); + -moz-outline-radius: 3px; + outline-offset: -2px; + } +} diff --git a/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css b/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css index a64e3a3f9ad4..e7d33f852936 100644 --- a/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css +++ b/browser/themes/winstripe/downloads/allDownloadsViewOverlay.css @@ -2,100 +2,23 @@ * 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/. */ -/*** Panel and outer controls ***/ - -#downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent { - padding: 0; -} - -#downloadsListBox { - background-color: transparent; - padding: 4px; - color: inherit; -} - -#downloadsPanel:not([hasdownloads]) > #downloadsListBox { - display: none; -} - -#downloadsHistory { - background: transparent; - color: -moz-nativehyperlinktext; - cursor: pointer; -} - -#downloadsHistory > .button-box { - margin: 1em; -} - -@media (-moz-windows-default-theme) { - #downloadsPanel[hasdownloads] > #downloadsFooter { -%ifdef WINSTRIPE_AERO - background-color: #f1f5fb; -%else - background-color: hsla(216,45%,88%,.98); -%endif - box-shadow: 0px 1px 2px rgb(204,214,234) inset; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - } -} - -/*** Downloads Summary and List items ***/ - -#downloadsSummary, -richlistitem[type="download"] { - height: 7em; - -moz-padding-end: 0; - color: inherit; -} - -#downloadsSummary { - padding: 8px 38px 8px 12px; - cursor: pointer; - -moz-user-focus: normal; -} - -#downloadsSummary:-moz-focusring { - outline: 1px -moz-dialogtext dotted; - outline-offset: -5px; -} - -#downloadsSummary > .downloadTypeIcon { - height: 24px; - width: 24px; - list-style-image: url("chrome://mozapps/skin/downloads/downloadIcon.png"); -} - -#downloadsSummaryDescription { - color: -moz-nativehyperlinktext; -} - -richlistitem[type="download"] { +#downloadsRichListBox { + -moz-appearance: none; margin: 0; - border-top: 1px solid hsla(0,0%,100%,.3); - border-bottom: 1px solid hsla(220,18%,51%,.25); - background: transparent; - padding: 8px; } -richlistitem[type="download"]:first-child { - border-top: 1px solid transparent; -} - -@media (-moz-windows-default-theme) { - richlistitem[type="download"]:last-child { - border-bottom: 1px solid transparent; - } -} - -#downloadsListBox:-moz-focusring > richlistitem[type="download"][selected] { - outline: 1px -moz-dialogtext dotted; - outline-offset: -1px; +#downloadsRichListBox > richlistitem.download { + height: 6em; +%ifndef WINSTRIPE_AERO + padding: 5px 8px; +%endif } .downloadTypeIcon { -moz-margin-end: 8px; +%ifdef WINSTRIPE_AERO + -moz-margin-start: 8px; +%endif /* Prevent flickering when changing states. */ min-height: 32px; min-width: 32px; @@ -106,214 +29,37 @@ richlistitem[type="download"]:first-child { } .downloadTarget { - margin-bottom: 6px; + margin-bottom: 3px; cursor: inherit; } .downloadDetails { - opacity: 0.6; - font-size: 90%; + opacity: 0.7; + font-size: 95%; cursor: inherit; } .downloadButton { -moz-appearance: none; + background: transparent; min-width: 0; min-height: 0; margin: 3px; border: none; - background: transparent; padding: 5px; list-style-image: url("chrome://browser/skin/downloads/buttons.png"); } -.downloadButton > .button-box { - padding: 0; -} - -/*** Highlighted list items ***/ - -richlistitem[type="download"][state="1"]:hover { - border-radius: 3px; - border-top: 1px solid hsla(0,0%,100%,.2); - border-bottom: 1px solid hsla(0,0%,0%,.2); - background-color: Highlight; - color: HighlightText; - cursor: pointer; -} - -/*** Button icons ***/ + /*** Button icons ***/ .downloadButton.downloadCancel { -moz-image-region: rect(0px, 16px, 16px, 0px); } -.downloadButton.downloadCancel:hover { - -moz-image-region: rect(0px, 32px, 16px, 16px); -} -.downloadButton.downloadCancel:active { - -moz-image-region: rect(0px, 48px, 16px, 32px); -} .downloadButton.downloadShow { -moz-image-region: rect(16px, 16px, 32px, 0px); } -.downloadButton.downloadShow:hover { - -moz-image-region: rect(16px, 32px, 32px, 16px); -} -.downloadButton.downloadShow:active { - -moz-image-region: rect(16px, 48px, 32px, 32px); -} .downloadButton.downloadRetry { -moz-image-region: rect(32px, 16px, 48px, 0px); } -.downloadButton.downloadRetry:hover { - -moz-image-region: rect(32px, 32px, 48px, 16px); -} -.downloadButton.downloadRetry:active { - -moz-image-region: rect(32px, 48px, 48px, 32px); -} - -%ifdef WINSTRIPE_AERO -@media not all and (-moz-windows-default-theme) { -%endif - -richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow { - -moz-image-region: rect(48px, 16px, 64px, 0px); -} -richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow:hover { - -moz-image-region: rect(48px, 32px, 64px, 16px); -} -richlistitem[type="download"][state="1"]:hover > .downloadButton.downloadShow:active { - -moz-image-region: rect(48px, 48px, 64px, 32px); -} - -%ifdef WINSTRIPE_AERO -} -%endif - -/*** Status and progress indicator ***/ - -#downloads-indicator-anchor { - /* Makes the outermost stack element positioned, so that its contents are - rendered over the main browser window in the Z order. This is required by - the animated event notification. */ - position: relative; -} - -/*** Main indicator icon ***/ - -#downloads-indicator-icon { - background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), - 0, 108, 18, 90) center no-repeat; - min-width: 18px; - min-height: 18px; -} - -#downloads-indicator-icon:-moz-lwtheme-brighttext { - background: -moz-image-rect(url("chrome://browser/skin/Toolbar-inverted.png"), - 0, 108, 18, 90) center no-repeat; -} - -#downloads-indicator[attention] > #downloads-indicator-anchor > #downloads-indicator-icon { - background-image: url("chrome://browser/skin/downloads/download-glow.png"); -} - -#downloads-indicator:not([counter]) > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter { - background: -moz-image-rect(url("chrome://browser/skin/Toolbar.png"), - 0, 108, 18, 90) center no-repeat; - background-size: 12px; -} - -#downloads-indicator:not([counter])[attention] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-counter { - background-image: url("chrome://browser/skin/downloads/download-glow.png"); -} - -/*** Event notification ***/ - -#downloads-indicator-notification { - opacity: 0; - background: url("chrome://browser/skin/downloads/download-notification.png") - center no-repeat; - background-size: 16px; -} - -@keyframes downloadsIndicatorNotificationRight { - from { opacity: 0; transform: translate(-128px, 128px) scale(8); } - 20% { opacity: .85; animation-timing-function: ease-out; } - to { opacity: 0; transform: translate(0) scale(1); } -} - -@keyframes downloadsIndicatorNotificationLeft { - from { opacity: 0; transform: translate(128px, 128px) scale(8); } - 20% { opacity: .85; animation-timing-function: ease-out; } - to { opacity: 0; transform: translate(0) scale(1); } -} - -#downloads-indicator[notification] > #downloads-indicator-anchor > #downloads-indicator-notification { - animation-name: downloadsIndicatorNotificationRight; - animation-duration: 1s; -} - -#downloads-indicator[notification]:-moz-locale-dir(rtl) > #downloads-indicator-anchor > #downloads-indicator-notification { - animation-name: downloadsIndicatorNotificationLeft; -} - -/*** Progress bar and text ***/ - -#downloads-indicator-counter { - height: 9px; - margin: -3px 0px 0px 0px; - color: hsl(0,0%,30%); - text-shadow: hsla(0,0%,100%,.5) 0 1px; - font-size: 9px; - line-height: 9px; - text-align: center; -} - -#downloads-indicator-counter:-moz-lwtheme-brighttext { - color: white; - text-shadow: 0 0 1px rgba(0,0,0,.7), - 0 1px 1.5px rgba(0,0,0,.5); -} - -#downloads-indicator-progress { - width: 16px; - height: 5px; - min-width: 0; - min-height: 0; - margin-top: 1px; - margin-bottom: 2px; - border-radius: 2px; - box-shadow: 0 1px 0 hsla(0,0%,100%,.4); -} - -#downloads-indicator-progress > .progress-bar { - -moz-appearance: none; - min-width: 0; - min-height: 0; - background-color: rgb(90, 201, 66); - background-image: linear-gradient(transparent 1px, rgba(255, 255, 255, 0.4) 1px, rgba(255, 255, 255, 0.4) 2px, transparent 2px); - border: 1px solid; - border-color: rgba(0,43,86,.6) rgba(0,43,86,.4) rgba(0,43,86,.4); - border-radius: 2px 0 0 2px; -} - -#downloads-indicator-progress > .progress-remainder { - -moz-appearance: none; - min-width: 0; - min-height: 0; - background-image: linear-gradient(#505050, #575757); - border: 1px solid; - border-color: hsla(0,0%,0%,.6) hsla(0,0%,0%,.4) hsla(0,0%,0%,.4); - -moz-border-start: none; - border-radius: 0 2px 2px 0; -} - -#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-bar { - background-color: rgb(220, 230, 81); -} - -#downloads-indicator[paused] > #downloads-indicator-anchor > #downloads-indicator-progress-area > #downloads-indicator-progress > .progress-remainder { - background-image: linear-gradient(#4b5000, #515700); -} diff --git a/browser/themes/winstripe/jar.mn b/browser/themes/winstripe/jar.mn index dd58ae1cb932..28594b9e36f4 100644 --- a/browser/themes/winstripe/jar.mn +++ b/browser/themes/winstripe/jar.mn @@ -278,7 +278,7 @@ browser.jar: skin/classic/aero/browser/downloads/download-glow.png (downloads/download-glow.png) skin/classic/aero/browser/downloads/download-notification.png (downloads/download-notification.png) * skin/classic/aero/browser/downloads/downloads.css (downloads/downloads-aero.css) - skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css) +* skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay-aero.css) skin/classic/aero/browser/feeds/feedIcon.png (feeds/feedIcon-aero.png) skin/classic/aero/browser/feeds/feedIcon16.png (feeds/feedIcon16-aero.png) skin/classic/aero/browser/feeds/audioFeedIcon.png (feeds/feedIcon-aero.png) From 827b117cbe1227cd7c7cfb9ef2bd059f2db17b34 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 21 Dec 2012 19:43:05 +0000 Subject: [PATCH 132/217] Crashtest for bug 813420 - "ASSERTION: Must not call under nsISVGChildFrame::ReflowSVG" with nested and dynamic marker. (Now WORKSFORME.) --- layout/svg/crashtests/813420-1.svg | 14 ++++++++++++++ layout/svg/crashtests/crashtests.list | 1 + 2 files changed, 15 insertions(+) create mode 100644 layout/svg/crashtests/813420-1.svg diff --git a/layout/svg/crashtests/813420-1.svg b/layout/svg/crashtests/813420-1.svg new file mode 100644 index 000000000000..d977c0e982fe --- /dev/null +++ b/layout/svg/crashtests/813420-1.svg @@ -0,0 +1,14 @@ + + + + + + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 82603acb04e7..9bb27a35a4f6 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -148,4 +148,5 @@ load 790072.svg load 791826-1.svg load 789390-1.html load 808318-1.svg +load 813420-1.svg From fec7a00db2406e12e77d70e93da9b8bdda1a186b Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 21 Dec 2012 12:44:42 -0700 Subject: [PATCH 133/217] Bug 823703 - Create and use an android xpcshell manifest; r=jmaher --- testing/testsuite-targets.mk | 2 +- testing/xpcshell/Makefile.in | 1 + testing/xpcshell/xpcshell_android.ini | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 testing/xpcshell/xpcshell_android.ini diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 9f77fac1fbe5..d5288a725abf 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -270,7 +270,7 @@ REMOTE_XPCSHELL = \ -I$(topsrcdir)/build \ -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \ $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ - --manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \ + --manifest=$(DEPTH)/_tests/xpcshell/xpcshell_android.ini \ --build-info-json=$(DEPTH)/mozinfo.json \ --no-logfiles \ --testing-modules-dir=$(call core_abspath,_tests/modules) \ diff --git a/testing/xpcshell/Makefile.in b/testing/xpcshell/Makefile.in index 6e0779cc8a83..8dea4634962c 100644 --- a/testing/xpcshell/Makefile.in +++ b/testing/xpcshell/Makefile.in @@ -56,6 +56,7 @@ PKG_STAGE = $(DIST)/test-package-stage libs:: $(INSTALL) $(srcdir)/xpcshell.ini $(DEPTH)/_tests/xpcshell $(INSTALL) $(srcdir)/xpcshell_b2g.ini $(DEPTH)/_tests/xpcshell + $(INSTALL) $(srcdir)/xpcshell_android.ini $(DEPTH)/_tests/xpcshell cp $(srcdir)/xpcshell.ini $(DEPTH)/_tests/xpcshell/all-test-dirs.list # Run selftests diff --git a/testing/xpcshell/xpcshell_android.ini b/testing/xpcshell/xpcshell_android.ini new file mode 100644 index 000000000000..b722eef3d4e9 --- /dev/null +++ b/testing/xpcshell/xpcshell_android.ini @@ -0,0 +1,5 @@ +; 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/. + +[include:gfx/tests/unit/xpcshell.ini] From 8a13e7b206c4b99667d8d1f8c4d85a75a7d09d7a Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Fri, 21 Dec 2012 12:44:45 -0700 Subject: [PATCH 134/217] Bug 823730 - Simplify make xpcshell-tests-remote make target; r=jmaher --- config/makefiles/xpcshell.mk | 6 +----- js/src/config/makefiles/xpcshell.mk | 6 +----- testing/testsuite-targets.mk | 28 +++++++++++----------------- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/config/makefiles/xpcshell.mk b/config/makefiles/xpcshell.mk index 55eca0d26f51..f45300578ea7 100644 --- a/config/makefiles/xpcshell.mk +++ b/config/makefiles/xpcshell.mk @@ -56,11 +56,7 @@ xpcshell-tests: xpcshell-tests-remote: DM_TRANS?=adb xpcshell-tests-remote: - $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ - -I$(topsrcdir)/build \ - -I$(topsrcdir)/build/mobile \ - -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \ - $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ + $(PYTHON) -u $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ --build-info-json=$(DEPTH)/mozinfo.json \ --testing-modules-dir=$(DEPTH)/_tests/modules \ diff --git a/js/src/config/makefiles/xpcshell.mk b/js/src/config/makefiles/xpcshell.mk index 55eca0d26f51..f45300578ea7 100644 --- a/js/src/config/makefiles/xpcshell.mk +++ b/js/src/config/makefiles/xpcshell.mk @@ -56,11 +56,7 @@ xpcshell-tests: xpcshell-tests-remote: DM_TRANS?=adb xpcshell-tests-remote: - $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ - -I$(topsrcdir)/build \ - -I$(topsrcdir)/build/mobile \ - -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \ - $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ + $(PYTHON) -u $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ --build-info-json=$(DEPTH)/mozinfo.json \ --testing-modules-dir=$(DEPTH)/_tests/modules \ diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index d5288a725abf..659b46d7d924 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -264,22 +264,6 @@ xpcshell-tests: $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) \ $(LIBXUL_DIST)/bin/xpcshell -REMOTE_XPCSHELL = \ - rm -f ./$@.log && \ - $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ - -I$(topsrcdir)/build \ - -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \ - $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ - --manifest=$(DEPTH)/_tests/xpcshell/xpcshell_android.ini \ - --build-info-json=$(DEPTH)/mozinfo.json \ - --no-logfiles \ - --testing-modules-dir=$(call core_abspath,_tests/modules) \ - --dm_trans=$(DM_TRANS) \ - --deviceIP=${TEST_DEVICE} \ - --objdir=$(DEPTH) \ - $(SYMBOLS_PATH) \ - $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) - B2G_XPCSHELL = \ rm -f ./@.log && \ $(PYTHON) -u $(topsrcdir)/config/pythonpath.py \ @@ -314,7 +298,17 @@ xpcshell-tests-b2g: xpcshell-tests-remote: DM_TRANS?=adb xpcshell-tests-remote: @if [ "${TEST_DEVICE}" != "" -o "$(DM_TRANS)" = "adb" ]; \ - then $(call REMOTE_XPCSHELL); $(CHECK_TEST_ERROR); \ + then $(PYTHON) -u $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \ + --manifest=$(DEPTH)/_tests/xpcshell/xpcshell_android.ini \ + --build-info-json=$(DEPTH)/mozinfo.json \ + --no-logfiles \ + --testing-modules-dir=$(call core_abspath,_tests/modules) \ + --dm_trans=$(DM_TRANS) \ + --deviceIP=${TEST_DEVICE} \ + --objdir=$(DEPTH) \ + $(SYMBOLS_PATH) \ + $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS); \ + $(CHECK_TEST_ERROR); \ else \ echo "please prepare your host with environment variables for TEST_DEVICE"; \ fi From 4ccc635ab957c25b62332ea05a4b6dd81863254e Mon Sep 17 00:00:00 2001 From: EKR Date: Fri, 21 Dec 2012 06:03:22 -0800 Subject: [PATCH 135/217] Bug 820102 - Clean up MediaPipeline threading (re-land after fix). r=derf,jesup --- media/mtransport/databuffer.h | 46 +++ media/mtransport/runnable_utils.h | 9 + .../signaling/src/media/VcmSIPCCBinding.cpp | 80 ++++-- .../src/mediapipeline/MediaPipeline.cpp | 265 +++++++++++------- .../src/mediapipeline/MediaPipeline.h | 227 +++++++++------ .../src/peerconnection/PeerConnectionCtx.cpp | 2 + .../src/peerconnection/PeerConnectionImpl.cpp | 29 +- .../src/peerconnection/PeerConnectionImpl.h | 4 +- .../peerconnection/PeerConnectionMedia.cpp | 23 -- .../src/peerconnection/PeerConnectionMedia.h | 28 +- .../signaling/test/mediapipeline_unittest.cpp | 31 +- 11 files changed, 459 insertions(+), 285 deletions(-) create mode 100644 media/mtransport/databuffer.h diff --git a/media/mtransport/databuffer.h b/media/mtransport/databuffer.h new file mode 100644 index 000000000000..f8f890b7f9a9 --- /dev/null +++ b/media/mtransport/databuffer.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +// Original author: ekr@rtfm.com + +#ifndef databuffer_h__ +#define databuffer_h__ +#include +#include +#include + +namespace mozilla { + +class DataBuffer { + public: + DataBuffer() : data_(nullptr), len_(0) {} + DataBuffer(const uint8_t *data, size_t len) { + Assign(data, len); + } + + void Assign(const uint8_t *data, size_t len) { + data_ = new unsigned char[ len ? len : 1]; // Don't depend on new [0]. + memcpy(static_cast(data_.get()), + static_cast(data), len); + len_ = len; + } + + const uint8_t *data() const { return data_; } + size_t len() const { return len_; } + const bool empty() const { return len_ != 0; } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataBuffer) + +private: + ScopedDeleteArray data_; + size_t len_; + + DISALLOW_COPY_ASSIGN(DataBuffer); +}; + +} + +#endif diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h index 09d0715ac953..cfcf213d4720 100644 --- a/media/mtransport/runnable_utils.h +++ b/media/mtransport/runnable_utils.h @@ -40,6 +40,15 @@ class runnable_args_base : public nsRunnable { // Temporary hack. Really we want to have a template which will do this #define RUN_ON_THREAD(t, r, h) ((t && (t != nsRefPtr(do_GetCurrentThread()))) ? t->Dispatch(r, h) : r->Run()) +#define ASSERT_ON_THREAD(t) do { \ + if (t) { \ + bool on; \ + nsresult rv; \ + rv = t->IsOnCurrentThread(&on); \ + MOZ_ASSERT(NS_SUCCEEDED(rv)); \ + MOZ_ASSERT(on); \ + } \ + } while(0) } #endif diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp index defc3d6307bd..d8a17bdfd0c6 100644 --- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp +++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp @@ -909,7 +909,7 @@ static short vcmCreateRemoteStream_m( hints |= nsDOMMediaStream::HINT_CONTENTS_VIDEO; } - sipcc::RemoteSourceStreamInfo* info; + nsRefPtr info; res = pc.impl()->CreateRemoteSourceStreamInfo(hints, &info); if (NS_FAILED(res)) { return VCM_ERROR; @@ -1329,14 +1329,26 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id, if (conduit->ConfigureRecvMediaCodecs(configs)) return VCM_ERROR; + // Now we have all the pieces, create the pipeline - stream->StorePipeline(pc_track_id, + mozilla::RefPtr pipeline = new mozilla::MediaPipelineReceiveAudio( + pc.impl()->GetHandle(), pc.impl()->GetMainThread().get(), pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow)); + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing audio pipeline"); + return VCM_ERROR; + } + + CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", + pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + + stream->StorePipeline(pc_track_id, pipeline); } else if (CC_IS_VIDEO(mcap_id)) { std::vector configs; @@ -1362,13 +1374,24 @@ static int vcmRxStartICE_m(cc_mcapid_t mcap_id, return VCM_ERROR; // Now we have all the pieces, create the pipeline - stream->StorePipeline(pc_track_id, - new mozilla::MediaPipelineReceiveVideo( - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow)); + mozilla::RefPtr pipeline = + new mozilla::MediaPipelineReceiveVideo( + pc.impl()->GetHandle(), + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing video pipeline"); + return VCM_ERROR; + } + + CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", + pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + + stream->StorePipeline(pc_track_id, pipeline); } else { CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__); return VCM_ERROR; @@ -1936,16 +1959,23 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id, if (!conduit || conduit->ConfigureSendMediaCodec(config)) return VCM_ERROR; - mozilla::RefPtr pipeline = - new mozilla::MediaPipelineTransmit( - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow); + mozilla::RefPtr pipeline = + new mozilla::MediaPipelineTransmit( + pc.impl()->GetHandle(), + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing audio pipeline"); + return VCM_ERROR; + } CSFLogDebug(logTag, "Created audio pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); + // Now we have all the pieces, create the pipeline stream->StorePipeline(pc_track_id, pipeline); @@ -1968,18 +1998,24 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id, if (!conduit || conduit->ConfigureSendMediaCodec(config)) return VCM_ERROR; - // Create the pipeline + // Now we have all the pieces, create the pipeline mozilla::RefPtr pipeline = new mozilla::MediaPipelineTransmit( - pc.impl()->GetMainThread().get(), - pc.impl()->GetSTSThread(), - stream->GetMediaStream(), - conduit, rtp_flow, rtcp_flow); + pc.impl()->GetHandle(), + pc.impl()->GetMainThread().get(), + pc.impl()->GetSTSThread(), + stream->GetMediaStream()->GetStream(), + conduit, rtp_flow, rtcp_flow); + + nsresult res = pipeline->Init(); + if (NS_FAILED(res)) { + CSFLogError(logTag, "Failure initializing video pipeline"); + return VCM_ERROR; + } CSFLogDebug(logTag, "Created video pipeline %p, conduit=%p, pc_stream=%d pc_track=%d", pipeline.get(), conduit.get(), pc_stream_id, pc_track_id); - // Now we have all the pieces, create the pipeline stream->StorePipeline(pc_track_id, pipeline); } else { CSFLogError(logTag, "%s: mcap_id unrecognized", __FUNCTION__); diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp index b8c90101bf8f..0efe968ac1ee 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.cpp @@ -23,6 +23,7 @@ #include "nsError.h" #include "AudioSegment.h" #include "MediaSegment.h" +#include "databuffer.h" #include "transportflow.h" #include "transportlayer.h" #include "transportlayerdtls.h" @@ -32,6 +33,14 @@ using namespace mozilla; +#ifdef DEBUG +// Dial up pipeline logging in debug mode +#define MP_LOG_INFO PR_LOG_WARN +#else +#define MP_LOG_INFO PR_LOG_INFO +#endif + + // Logging context MOZ_MTLOG_MODULE("mediapipeline"); @@ -40,6 +49,7 @@ namespace mozilla { static char kDTLSExporterLabel[] = "EXTRACTOR-dtls_srtp"; nsresult MediaPipeline::Init() { + ASSERT_ON_THREAD(main_thread_); conduit_->AttachTransport(transport_); MOZ_ASSERT(rtp_transport_); @@ -52,7 +62,10 @@ nsresult MediaPipeline::Init() { if (rtp_transport_->state() == TransportLayer::TS_OPEN) { res = TransportReady(rtp_transport_); - NS_ENSURE_SUCCESS(res, res); + if (NS_FAILED(res)) { + MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()"); + return res; + } } else { if (!muxed_) { rtcp_transport_->SignalStateChange.connect(this, @@ -60,7 +73,10 @@ nsresult MediaPipeline::Init() { if (rtcp_transport_->state() == TransportLayer::TS_OPEN) { res = TransportReady(rtcp_transport_); - NS_ENSURE_SUCCESS(res, res); + if (NS_FAILED(res)) { + MOZ_MTLOG(PR_LOG_ERROR, "Error calling TransportReady()"); + return res; + } } } } @@ -70,7 +86,9 @@ nsresult MediaPipeline::Init() { // Disconnect us from the transport so that we can cleanly destruct // the pipeline on the main thread. -void MediaPipeline::DetachTransportInt() { +void MediaPipeline::DetachTransport_s() { + ASSERT_ON_THREAD(sts_thread_); + transport_->Detach(); rtp_transport_ = NULL; rtcp_transport_ = NULL; @@ -78,13 +96,13 @@ void MediaPipeline::DetachTransportInt() { void MediaPipeline::DetachTransport() { RUN_ON_THREAD(sts_thread_, - WrapRunnable(this, &MediaPipeline::DetachTransportInt), + WrapRunnable(this, &MediaPipeline::DetachTransport_s), NS_DISPATCH_SYNC); } void MediaPipeline::StateChange(TransportFlow *flow, TransportLayer::State state) { if (state == TransportLayer::TS_OPEN) { - MOZ_MTLOG(PR_LOG_DEBUG, "Flow is ready"); + MOZ_MTLOG(MP_LOG_INFO, "Flow is ready"); TransportReady(flow); } else if (state == TransportLayer::TS_CLOSED || state == TransportLayer::TS_ERROR) { @@ -108,18 +126,21 @@ nsresult MediaPipeline::TransportReady(TransportFlow *flow) { } nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { + MOZ_ASSERT(!description_.empty()); bool rtcp = !(flow == rtp_transport_); State *state = rtcp ? &rtcp_state_ : &rtp_state_; if (*state != MP_CONNECTING) { MOZ_MTLOG(PR_LOG_ERROR, "Transport ready for flow in wrong state:" << - (rtcp ? "rtcp" : "rtp")); + description_ << ": " << (rtcp ? "rtcp" : "rtp")); return NS_ERROR_FAILURE; } nsresult res; - MOZ_MTLOG(PR_LOG_DEBUG, "Transport ready for flow " << (rtcp ? "rtcp" : "rtp")); + MOZ_MTLOG(MP_LOG_INFO, "Transport ready for pipeline " << + static_cast(this) << " flow " << description_ << ": " << + (rtcp ? "rtcp" : "rtp")); // Now instantiate the SRTP objects TransportLayerDtls *dtls = static_cast( @@ -192,14 +213,14 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { rtcp_send_srtp_ = rtp_send_srtp_; rtcp_recv_srtp_ = rtp_recv_srtp_; - MOZ_MTLOG(PR_LOG_DEBUG, "Listening for packets received on " << + MOZ_MTLOG(MP_LOG_INFO, "Listening for packets received on " << static_cast(dtls->downward())); dtls->downward()->SignalPacketReceived.connect(this, &MediaPipeline:: PacketReceived); } else { - MOZ_MTLOG(PR_LOG_DEBUG, "Listening for RTP packets received on " << + MOZ_MTLOG(MP_LOG_INFO, "Listening for RTP packets received on " << static_cast(dtls->downward())); dtls->downward()->SignalPacketReceived.connect(this, @@ -219,7 +240,7 @@ nsresult MediaPipeline::TransportReadyInt(TransportFlow *flow) { return NS_ERROR_FAILURE; } - MOZ_MTLOG(PR_LOG_DEBUG, "Listening for RTCP packets received on " << + MOZ_MTLOG(MP_LOG_INFO, "Listening for RTCP packets received on " << static_cast(dtls->downward())); // Start listening @@ -239,7 +260,7 @@ nsresult MediaPipeline::TransportFailed(TransportFlow *flow) { *state = MP_CLOSED; - MOZ_MTLOG(PR_LOG_DEBUG, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp")); + MOZ_MTLOG(MP_LOG_INFO, "Transport closed for flow " << (rtcp ? "rtcp" : "rtp")); NS_WARNING( "MediaPipeline Transport failed. This is not properly cleaned up yet"); @@ -254,25 +275,10 @@ nsresult MediaPipeline::TransportFailed(TransportFlow *flow) { } -// Wrapper to send a packet on the STS thread. nsresult MediaPipeline::SendPacket(TransportFlow *flow, const void *data, int len) { - nsresult rv; - nsresult res; + ASSERT_ON_THREAD(sts_thread_); - rv = RUN_ON_THREAD(sts_thread_, - WrapRunnableRet(this, &MediaPipeline::SendPacketInt, flow, data, len, &res), - NS_DISPATCH_SYNC); - - // res is invalid unless the dispatch succeeded - if (NS_FAILED(rv)) - return rv; - - return res; -} - -nsresult MediaPipeline::SendPacketInt(TransportFlow *flow, const void *data, - int len) { // Note that we bypass the DTLS layer here TransportLayerDtls *dtls = static_cast( flow->GetLayer(TransportLayerDtls::ID())); @@ -295,37 +301,45 @@ nsresult MediaPipeline::SendPacketInt(TransportFlow *flow, const void *data, void MediaPipeline::increment_rtp_packets_sent() { ++rtp_packets_sent_; - if (!(rtp_packets_sent_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTP packet count " << static_cast(this) + + if (!(rtp_packets_sent_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTP sent packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtp_transport_) << ": " << rtp_packets_sent_); } } void MediaPipeline::increment_rtcp_packets_sent() { ++rtcp_packets_sent_; - if (!(rtcp_packets_sent_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTCP packet count " << static_cast(this) + if (!(rtcp_packets_sent_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTCP sent packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtcp_transport_) << ": " << rtcp_packets_sent_); } } void MediaPipeline::increment_rtp_packets_received() { ++rtp_packets_received_; - if (!(rtp_packets_received_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTP packet count " << static_cast(this) + if (!(rtp_packets_received_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTP received packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtp_transport_) << ": " << rtp_packets_received_); } } void MediaPipeline::increment_rtcp_packets_received() { ++rtcp_packets_received_; - if (!(rtcp_packets_received_ % 1000)) { - MOZ_MTLOG(PR_LOG_DEBUG, "RTCP packet count " << static_cast(this) + if (!(rtcp_packets_received_ % 100)) { + MOZ_MTLOG(MP_LOG_INFO, "RTCP received packet count for " << description_ + << " Pipeline " << static_cast(this) + << " Flow : " << static_cast(rtcp_transport_) << ": " << rtcp_packets_received_); } } - void MediaPipeline::RtpPacketReceived(TransportLayer *layer, const unsigned char *data, size_t len) { @@ -334,9 +348,10 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer, return; } - // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived - // TODO(ekr@rtfm.com): filter on SSRC for bundle - increment_rtp_packets_received(); + if (!conduit_) { + MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; media disconnected"); + return; + } MOZ_ASSERT(rtp_recv_srtp_); // This should never happen @@ -346,6 +361,10 @@ void MediaPipeline::RtpPacketReceived(TransportLayer *layer, return; } + // TODO(ekr@rtfm.com): filter for DTLS here and in RtcpPacketReceived + // TODO(ekr@rtfm.com): filter on SSRC for bundle + increment_rtp_packets_received(); + // Make a copy rather than cast away constness ScopedDeletePtr inner_data( new unsigned char[len]); @@ -367,6 +386,17 @@ void MediaPipeline::RtcpPacketReceived(TransportLayer *layer, return; } + if (!conduit_) { + MOZ_MTLOG(PR_LOG_DEBUG, "Discarding incoming packet; media disconnected"); + return; + } + + if (direction_ == RECEIVE) { + // Discard any RTCP that is being transmitted to us + // This will be unnecessary when we have SSRC filtering. + return; + } + increment_rtcp_packets_received(); MOZ_ASSERT(rtcp_recv_srtp_); // This should never happen @@ -434,25 +464,55 @@ void MediaPipeline::PacketReceived(TransportLayer *layer, } nsresult MediaPipelineTransmit::Init() { + ASSERT_ON_THREAD(main_thread_); + + description_ = pc_ + "| "; + description_ += conduit_->type() == MediaSessionConduit::AUDIO ? + "Transmit audio" : "Transmit video"; + // TODO(ekr@rtfm.com): Check for errors MOZ_MTLOG(PR_LOG_DEBUG, "Attaching pipeline to stream " << static_cast(stream_) << " conduit type=" << (conduit_->type() == MediaSessionConduit::AUDIO ? - "audio" : "video") << - " hints=" << stream_->GetHintContents()); + "audio" : "video")); - // Force this to be a refptr so that we are holding a strong reference - // to the media stream. - nsRefPtr stream (stream_->GetStream()); - return RUN_ON_THREAD(main_thread_, WrapRunnable(stream, - &MediaStream::AddListener, - listener_), - NS_DISPATCH_NORMAL); + stream_->AddListener(listener_); + + return MediaPipeline::Init(); +} + +nsresult MediaPipelineTransmit::TransportReady(TransportFlow *flow) { + // Call base ready function. + MediaPipeline::TransportReady(flow); + + if (flow == rtp_transport_) { + // TODO(ekr@rtfm.com): Move onto MSG thread. + listener_->SetActive(true); + } + + return NS_OK; } nsresult MediaPipeline::PipelineTransport::SendRtpPacket( const void *data, int len) { + nsresult ret; + + nsAutoPtr buf(new DataBuffer(static_cast(data), + len)); + + RUN_ON_THREAD(sts_thread_, + WrapRunnableRet( + RefPtr(this), + &MediaPipeline::PipelineTransport::SendRtpPacket_s, + buf, &ret), + NS_DISPATCH_NORMAL); + + return NS_OK; +} + +nsresult MediaPipeline::PipelineTransport::SendRtpPacket_s( + nsAutoPtr data) { if (!pipeline_) return NS_OK; // Detached @@ -467,14 +527,15 @@ nsresult MediaPipeline::PipelineTransport::SendRtpPacket( // libsrtp enciphers in place, so we need a new, big enough // buffer. // XXX. allocates and deletes one buffer per packet sent. - int max_len = len + SRTP_MAX_EXPANSION; + // Bug 822129 + int max_len = data->len() + SRTP_MAX_EXPANSION; ScopedDeletePtr inner_data( new unsigned char[max_len]); - memcpy(inner_data, data, len); + memcpy(inner_data, data->data(), data->len()); int out_len; nsresult res = pipeline_->rtp_send_srtp_->ProtectRtp(inner_data, - len, + data->len(), max_len, &out_len); if (!NS_SUCCEEDED(res)) @@ -487,6 +548,23 @@ nsresult MediaPipeline::PipelineTransport::SendRtpPacket( nsresult MediaPipeline::PipelineTransport::SendRtcpPacket( const void *data, int len) { + nsresult ret; + + nsAutoPtr buf(new DataBuffer(static_cast(data), + len)); + + RUN_ON_THREAD(sts_thread_, + WrapRunnableRet( + RefPtr(this), + &MediaPipeline::PipelineTransport::SendRtcpPacket_s, + buf, &ret), + NS_DISPATCH_NORMAL); + + return NS_OK; +} + +nsresult MediaPipeline::PipelineTransport::SendRtcpPacket_s( + nsAutoPtr data) { if (!pipeline_) return NS_OK; // Detached @@ -501,14 +579,15 @@ nsresult MediaPipeline::PipelineTransport::SendRtcpPacket( // libsrtp enciphers in place, so we need a new, big enough // buffer. // XXX. allocates and deletes one buffer per packet sent. - int max_len = len + SRTP_MAX_EXPANSION; + // Bug 822129. + int max_len = data->len() + SRTP_MAX_EXPANSION; ScopedDeletePtr inner_data( new unsigned char[max_len]); - memcpy(inner_data, data, len); + memcpy(inner_data, data->data(), data->len()); int out_len; nsresult res = pipeline_->rtcp_send_srtp_->ProtectRtcp(inner_data, - len, + data->len(), max_len, &out_len); if (!NS_SUCCEEDED(res)) @@ -525,22 +604,18 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, TrackTicks offset, uint32_t events, const MediaSegment& queued_media) { - if (!pipeline_) - return; // Detached - MOZ_MTLOG(PR_LOG_DEBUG, "MediaPipeline::NotifyQueuedTrackChanges()"); - // Return early if we are not connected to avoid queueing stuff - // up in the conduit - if (pipeline_->rtp_transport_->state() != TransportLayer::TS_OPEN) { - MOZ_MTLOG(PR_LOG_DEBUG, "Transport not ready yet, dropping packets"); + if (!active_) { + MOZ_MTLOG(PR_LOG_DEBUG, "Discarding packets because transport not ready"); return; } // TODO(ekr@rtfm.com): For now assume that we have only one // track type and it's destined for us + // See bug 784517 if (queued_media.GetType() == MediaSegment::AUDIO) { - if (pipeline_->conduit_->type() != MediaSessionConduit::AUDIO) { + if (conduit_->type() != MediaSessionConduit::AUDIO) { // Ignore data in case we have a muxed stream return; } @@ -549,14 +624,13 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, AudioSegment::ChunkIterator iter(*audio); while(!iter.IsEnded()) { - pipeline_->ProcessAudioChunk(static_cast - (pipeline_->conduit_.get()), - rate, *iter); + ProcessAudioChunk(static_cast(conduit_.get()), + rate, *iter); iter.Next(); } } else if (queued_media.GetType() == MediaSegment::VIDEO) { #ifdef MOZILLA_INTERNAL_API - if (pipeline_->conduit_->type() != MediaSessionConduit::VIDEO) { + if (conduit_->type() != MediaSessionConduit::VIDEO) { // Ignore data in case we have a muxed stream return; } @@ -565,9 +639,8 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, VideoSegment::ChunkIterator iter(*video); while(!iter.IsEnded()) { - pipeline_->ProcessVideoChunk(static_cast - (pipeline_->conduit_.get()), - rate, *iter); + ProcessVideoChunk(static_cast(conduit_.get()), + rate, *iter); iter.Next(); } #endif @@ -576,9 +649,10 @@ NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, } } -void MediaPipelineTransmit::ProcessAudioChunk(AudioSessionConduit *conduit, - TrackRate rate, - AudioChunk& chunk) { +void MediaPipelineTransmit::PipelineListener::ProcessAudioChunk( + AudioSessionConduit *conduit, + TrackRate rate, + AudioChunk& chunk) { // TODO(ekr@rtfm.com): Do more than one channel nsAutoArrayPtr samples(new int16_t[chunk.mDuration]); @@ -612,9 +686,10 @@ void MediaPipelineTransmit::ProcessAudioChunk(AudioSessionConduit *conduit, } #ifdef MOZILLA_INTERNAL_API -void MediaPipelineTransmit::ProcessVideoChunk(VideoSessionConduit *conduit, - TrackRate rate, - VideoChunk& chunk) { +void MediaPipelineTransmit::PipelineListener::ProcessVideoChunk( + VideoSessionConduit* conduit, + TrackRate rate, + VideoChunk& chunk) { // We now need to send the video frame to the other side layers::Image *img = chunk.mFrame.GetImage(); if (!img) { @@ -667,27 +742,20 @@ void MediaPipelineTransmit::ProcessVideoChunk(VideoSessionConduit *conduit, #endif nsresult MediaPipelineReceiveAudio::Init() { + ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(PR_LOG_DEBUG, __FUNCTION__); - // Force this to be a refptr so that we are holding a strong reference - // to the media stream. - nsRefPtr stream (stream_->GetStream()); - return RUN_ON_THREAD(main_thread_, WrapRunnable(stream, - &MediaStream::AddListener, - listener_), - NS_DISPATCH_NORMAL); + description_ = pc_ + "| Receive audio"; + + stream_->AddListener(listener_); + + return MediaPipelineReceive::Init(); } void MediaPipelineReceiveAudio::PipelineListener:: NotifyPull(MediaStreamGraph* graph, StreamTime total) { - if (!pipeline_) - return; // Detached - - SourceMediaStream *source = - pipeline_->stream_->GetStream()->AsSourceStream(); - - MOZ_ASSERT(source); - if (!source) { + MOZ_ASSERT(source_); + if (!source_) { MOZ_MTLOG(PR_LOG_ERROR, "NotifyPull() called from a non-SourceMediaStream"); return; } @@ -715,7 +783,7 @@ NotifyPull(MediaStreamGraph* graph, StreamTime total) { int samples_length; MediaConduitErrorCode err = - static_cast(pipeline_->conduit_.get())->GetAudioFrame( + static_cast(conduit_.get())->GetAudioFrame( static_cast(samples->Data()), 16000, // Sampling rate fixed at 16 kHz for now 0, // TODO(ekr@rtfm.com): better estimate of capture delay @@ -731,20 +799,21 @@ NotifyPull(MediaStreamGraph* graph, StreamTime total) { segment.AppendFrames(samples.forget(), samples_length, 0, samples_length, AUDIO_FORMAT_S16); - char buf[32]; - PR_snprintf(buf, 32, "%p", source); - source->AppendToTrack(1, // TODO(ekr@rtfm.com): Track ID - &segment); + source_->AppendToTrack(1, // TODO(ekr@rtfm.com): Track ID + &segment); } } nsresult MediaPipelineReceiveVideo::Init() { + ASSERT_ON_THREAD(main_thread_); MOZ_MTLOG(PR_LOG_DEBUG, __FUNCTION__); + description_ = pc_ + "| Receive video"; + static_cast(conduit_.get())-> AttachRenderer(renderer_); - return NS_OK; + return MediaPipelineReceive::Init(); } MediaPipelineReceiveVideo::PipelineRenderer::PipelineRenderer( @@ -755,7 +824,7 @@ MediaPipelineReceiveVideo::PipelineRenderer::PipelineRenderer( #ifdef MOZILLA_INTERNAL_API image_container_ = layers::LayerManager::CreateImageContainer(); SourceMediaStream *source = - pipeline_->stream_->GetStream()->AsSourceStream(); + pipeline_->stream_->AsSourceStream(); source->AddTrack(1 /* Track ID */, 30, 0, new VideoSegment()); source->AdvanceKnownTracksTime(STREAM_TIME_MAX); #endif @@ -768,7 +837,7 @@ void MediaPipelineReceiveVideo::PipelineRenderer::RenderVideoFrame( int64_t render_time) { #ifdef MOZILLA_INTERNAL_API SourceMediaStream *source = - pipeline_->stream_->GetStream()->AsSourceStream(); + pipeline_->stream_->AsSourceStream(); // Create a video frame and append it to the track. ImageFormat format = PLANAR_YCBCR; diff --git a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h index c1738b78ae89..787b75f47995 100644 --- a/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h +++ b/media/webrtc/signaling/src/mediapipeline/MediaPipeline.h @@ -17,6 +17,8 @@ #include "MediaConduitInterface.h" #include "AudioSegment.h" #include "SrtpFlow.h" +#include "databuffer.h" +#include "runnable_utils.h" #include "transportflow.h" #ifdef MOZILLA_INTERNAL_API @@ -35,14 +37,35 @@ namespace mozilla { // network -> transport -> [us] -> conduit -> [us] -> stream -> Playout // // The boxes labeled [us] are just bridge logic implemented in this class +// +// We have to deal with a number of threads: +// +// GSM: +// * Assembles the pipeline +// SocketTransportService +// * Receives notification that ICE and DTLS have completed +// * Processes incoming network data and passes it to the conduit +// * Processes outgoing RTP and RTCP +// MediaStreamGraph +// * Receives outgoing data from the MediaStreamGraph +// * Receives pull requests for more data from the +// MediaStreamGraph +// One or another GIPS threads +// * Receives RTCP messages to send to the other side +// * Processes video frames GIPS wants to render +// +// For a transmitting conduit, "output" is RTP and "input" is RTCP. +// For a receiving conduit, "input" is RTP and "output" is RTCP. +// class MediaPipeline : public sigslot::has_slots<> { public: enum Direction { TRANSMIT, RECEIVE }; enum State { MP_CONNECTING, MP_OPEN, MP_CLOSED }; - MediaPipeline(Direction direction, + MediaPipeline(const std::string& pc, + Direction direction, nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) @@ -64,21 +87,31 @@ class MediaPipeline : public sigslot::has_slots<> { rtcp_packets_sent_(0), rtp_packets_received_(0), rtcp_packets_received_(0), - muxed_((rtcp_transport_ == NULL) || (rtp_transport_ == rtcp_transport_)) { - Init(); + muxed_((rtcp_transport_ == NULL) || (rtp_transport_ == rtcp_transport_)), + pc_(pc), + description_() { } virtual ~MediaPipeline() { + MOZ_ASSERT(!stream_); // Check that we have shut down already. + } + + void Shutdown() { + ASSERT_ON_THREAD(main_thread_); + // First shut down networking and then disconnect from + // the media streams. DetachTransport() is sync so + // we are sure that the transport is shut down before + // we touch stream_ or conduit_. DetachTransport(); + if (stream_) { + DetachMediaStream(); + } } virtual nsresult Init(); virtual Direction direction() const { return direction_; } - virtual void DetachMediaStream() {} - virtual void DetachTransport(); - int rtp_packets_sent() const { return rtp_packets_sent_; } int rtcp_packets_sent() const { return rtp_packets_sent_; } int rtp_packets_received() const { return rtp_packets_received_; } @@ -87,13 +120,17 @@ class MediaPipeline : public sigslot::has_slots<> { // Thread counting NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPipeline) - protected: + protected: + virtual void DetachMediaStream() {} + // Separate class to allow ref counting class PipelineTransport : public TransportInterface { public: // Implement the TransportInterface functions PipelineTransport(MediaPipeline *pipeline) - : pipeline_(pipeline) {} + : pipeline_(pipeline), + sts_thread_(pipeline->sts_thread_) {} + void Detach() { pipeline_ = NULL; } MediaPipeline *pipeline() const { return pipeline_; } @@ -101,7 +138,11 @@ class MediaPipeline : public sigslot::has_slots<> { virtual nsresult SendRtcpPacket(const void* data, int len); private: + virtual nsresult SendRtpPacket_s(nsAutoPtr data); + virtual nsresult SendRtcpPacket_s(nsAutoPtr data); + MediaPipeline *pipeline_; // Raw pointer to avoid cycles + nsCOMPtr sts_thread_; }; friend class PipelineTransport; @@ -124,31 +165,51 @@ class MediaPipeline : public sigslot::has_slots<> { void PacketReceived(TransportLayer *layer, const unsigned char *data, size_t len); - Direction direction_; - nsDOMMediaStream* stream_; - RefPtr conduit_; + RefPtr stream_; // A pointer to the stream we are servicing. + // Written on the main thread. + // Used on STS and MediaStreamGraph threads. + RefPtr conduit_; // Our conduit. Written on the main + // thread. Read on STS thread. + + // The transport objects. Read/written on STS thread. RefPtr rtp_transport_; State rtp_state_; RefPtr rtcp_transport_; State rtcp_state_; + + // Pointers to the threads we need. Initialized at creation + // and used all over the place. nsCOMPtr main_thread_; nsCOMPtr sts_thread_; + + // Created on Init. Referenced by the conduit and eventually + // destroyed on the STS thread. RefPtr transport_; - bool transport_connected_; + + // Used only on STS thread. RefPtr rtp_send_srtp_; RefPtr rtcp_send_srtp_; RefPtr rtp_recv_srtp_; RefPtr rtcp_recv_srtp_; + + // Written only on STS thread. May be read on other + // threads but since there is no mutex, the values + // will only be approximate. int rtp_packets_sent_; int rtcp_packets_sent_; int rtp_packets_received_; int rtcp_packets_received_; + + // Written on Init. Read on STS thread. bool muxed_; + std::string pc_; + std::string description_; private: - virtual void DetachTransportInt(); - nsresult SendPacketInt(TransportFlow *flow, const void* data, int len); + void DetachTransport(); + void DetachTransport_s(); + nsresult TransportReadyInt(TransportFlow *flow); bool IsRtp(const unsigned char *data, size_t len); @@ -159,47 +220,44 @@ class MediaPipeline : public sigslot::has_slots<> { // and transmitting to the network. class MediaPipelineTransmit : public MediaPipeline { public: - MediaPipelineTransmit(nsCOMPtr main_thread, + MediaPipelineTransmit(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipeline(TRANSMIT, main_thread, sts_thread, + MediaPipeline(pc, TRANSMIT, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), - listener_(new PipelineListener(this)) { - Init(); // TODO(ekr@rtfm.com): ignoring error - } + listener_(new PipelineListener(conduit)) {} // Initialize (stuff here may fail) - nsresult Init(); - - virtual ~MediaPipelineTransmit() { - if (stream_ && listener_){ - stream_->GetStream()->RemoveListener(listener_); - - // These shouldn't be necessary, but just to make sure - // that if we have messed up ownership somehow the - // interfaces just abort. - listener_->Detach(); - } - } + virtual nsresult Init(); + // Called on the main thread. virtual void DetachMediaStream() { - // TODO(ekr@rtfm.com): Are multiple removes a problem? - stream_->GetStream()->RemoveListener(listener_); - stream_ = NULL; - listener_->Detach(); + ASSERT_ON_THREAD(main_thread_); + stream_->RemoveListener(listener_); + // Remove our reference so that when the MediaStreamGraph + // releases the listener, it will be destroyed. + listener_ = nullptr; + stream_ = nullptr; } + // Override MediaPipeline::TransportReady. + virtual nsresult TransportReady(TransportFlow *flow); + // Separate class to allow ref counting class PipelineListener : public MediaStreamListener { public: - PipelineListener(MediaPipelineTransmit *pipeline) : - pipeline_(pipeline) {} - void Detach() { pipeline_ = NULL; } + PipelineListener(const RefPtr& conduit) + : conduit_(conduit), active_(false) {} + // XXX. This is not thread-safe but the hazard is just + // that active_ = true takes a while to propagate. Revisit + // when 823600 lands. + void SetActive(bool active) { active_ = active; } // Implement MediaStreamListener virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, @@ -210,18 +268,19 @@ class MediaPipelineTransmit : public MediaPipeline { virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {} private: - MediaPipelineTransmit *pipeline_; // Raw pointer to avoid cycles + virtual void ProcessAudioChunk(AudioSessionConduit *conduit, + TrackRate rate, AudioChunk& chunk); +#ifdef MOZILLA_INTERNAL_API + virtual void ProcessVideoChunk(VideoSessionConduit *conduit, + TrackRate rate, VideoChunk& chunk); +#endif + RefPtr conduit_; + volatile bool active_; }; - friend class PipelineListener; private: - virtual void ProcessAudioChunk(AudioSessionConduit *conduit, - TrackRate rate, AudioChunk& chunk); -#ifdef MOZILLA_INTERNAL_API - virtual void ProcessVideoChunk(VideoSessionConduit *conduit, - TrackRate rate, VideoChunk& chunk); -#endif - RefPtr listener_; +RefPtr listener_; + }; @@ -229,13 +288,14 @@ class MediaPipelineTransmit : public MediaPipeline { // rendering video. class MediaPipelineReceive : public MediaPipeline { public: - MediaPipelineReceive(nsCOMPtr main_thread, + MediaPipelineReceive(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipeline(RECEIVE, main_thread, sts_thread, + MediaPipeline(pc, RECEIVE, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), segments_added_(0) { @@ -254,41 +314,40 @@ class MediaPipelineReceive : public MediaPipeline { // rendering audio. class MediaPipelineReceiveAudio : public MediaPipelineReceive { public: - MediaPipelineReceiveAudio(nsCOMPtr main_thread, + MediaPipelineReceiveAudio(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipelineReceive(main_thread, sts_thread, + MediaPipelineReceive(pc, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), - listener_(new PipelineListener(this)) { - Init(); - } - - ~MediaPipelineReceiveAudio() { - if (stream_ && listener_) { - stream_->GetStream()->RemoveListener(listener_); - listener_->Detach(); - } + listener_(new PipelineListener(stream->AsSourceStream(), + conduit)) { } virtual void DetachMediaStream() { - // TODO(ekr@rtfm.com): Are multiple removes a problem? - stream_->GetStream()->RemoveListener(listener_); - stream_ = NULL; - listener_->Detach(); + ASSERT_ON_THREAD(main_thread_); + stream_->RemoveListener(listener_); + // Remove our reference so that when the MediaStreamGraph + // releases the listener, it will be destroyed. + listener_ = nullptr; + stream_ = nullptr; } + virtual nsresult Init(); + private: // Separate class to allow ref counting class PipelineListener : public MediaStreamListener { public: - PipelineListener(MediaPipelineReceiveAudio *pipeline) - : pipeline_(pipeline), - played_(0) {} - void Detach() { pipeline_ = NULL; } + PipelineListener(SourceMediaStream * source, + const RefPtr& conduit) + : source_(source), + conduit_(conduit), + played_(0) {} // Implement MediaStreamListener virtual void NotifyQueuedTrackChanges(MediaStreamGraph* graph, TrackID tid, @@ -299,12 +358,10 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime); private: - MediaPipelineReceiveAudio *pipeline_; // Raw pointer to avoid cycles + SourceMediaStream *source_; + RefPtr conduit_; StreamTime played_; }; - friend class PipelineListener; - - nsresult Init(); RefPtr listener_; }; @@ -314,22 +371,29 @@ class MediaPipelineReceiveAudio : public MediaPipelineReceive { // rendering video. class MediaPipelineReceiveVideo : public MediaPipelineReceive { public: - MediaPipelineReceiveVideo(nsCOMPtr main_thread, + MediaPipelineReceiveVideo(const std::string& pc, + nsCOMPtr main_thread, nsCOMPtr sts_thread, - nsDOMMediaStream* stream, + MediaStream *stream, RefPtr conduit, RefPtr rtp_transport, RefPtr rtcp_transport) : - MediaPipelineReceive(main_thread, sts_thread, + MediaPipelineReceive(pc, main_thread, sts_thread, stream, conduit, rtp_transport, rtcp_transport), renderer_(new PipelineRenderer(this)) { - Init(); } - ~MediaPipelineReceiveVideo() { + // Called on the main thread. + virtual void DetachMediaStream() { + ASSERT_ON_THREAD(main_thread_); + conduit_ = nullptr; // Force synchronous destruction so we + // stop generating video. + stream_ = nullptr; } + virtual nsresult Init(); + private: class PipelineRenderer : public VideoRenderer { public: @@ -360,7 +424,6 @@ class MediaPipelineReceiveVideo : public MediaPipelineReceive { }; friend class PipelineRenderer; - nsresult Init(); RefPtr renderer_; }; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index 29e13a9089fa..cc7e2b72596a 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -31,7 +31,9 @@ nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread) { gMainThread = mainThread; CSF::VcmSIPCCBinding::setMainThread(gMainThread); } else { +#ifdef MOZILLA_INTERNAL_API MOZ_ASSERT(gMainThread == mainThread); +#endif } nsresult res; diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 55417fe2cc95..b9930600ff06 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -260,19 +260,7 @@ PeerConnectionImpl::MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aRetval) } nsresult -PeerConnectionImpl::MakeRemoteSource(nsDOMMediaStream* aStream, RemoteSourceStreamInfo** aInfo) -{ - MOZ_ASSERT(aInfo); - MOZ_ASSERT(aStream); - - // TODO(ekr@rtfm.com): Add the track info with the first segment - nsRefPtr remote = new RemoteSourceStreamInfo(aStream); - NS_ADDREF(*aInfo = remote); - return NS_OK; -} - -nsresult -PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStreamInfo** aInfo) +PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, nsRefPtr* aInfo) { MOZ_ASSERT(aInfo); PC_AUTO_ENTER_API_CALL_NO_CHECK(); @@ -288,19 +276,8 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStr static_cast(comstream->GetStream())->SetPullEnabled(true); nsRefPtr remote; - if (!mThread || NS_IsMainThread()) { - remote = new RemoteSourceStreamInfo(comstream); - NS_ADDREF(*aInfo = remote); - return NS_OK; - } - - mThread->Dispatch(WrapRunnableNMRet( - &PeerConnectionImpl::MakeRemoteSource, comstream, aInfo, &res - ), NS_DISPATCH_SYNC); - - if (NS_FAILED(res)) { - return res; - } + remote = new RemoteSourceStreamInfo(comstream); + *aInfo = remote; return NS_OK; } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 57f9f5231201..f393a2c16df6 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -119,14 +119,14 @@ public: static nsresult ConvertConstraints( const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx); static nsresult MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aStream); - static nsresult MakeRemoteSource(nsDOMMediaStream* aStream, RemoteSourceStreamInfo** aInfo); Role GetRole() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mRole; } - nsresult CreateRemoteSourceStreamInfo(uint32_t aHint, RemoteSourceStreamInfo** aInfo); + nsresult CreateRemoteSourceStreamInfo(uint32_t aHint, + nsRefPtr* aInfo); // Implementation of the only observer we need virtual void onCallEvent( diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index c7c404e41dbd..774e849858a6 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -28,22 +28,6 @@ static const char* logTag = "PeerConnectionMedia"; static const mozilla::TrackID TRACK_AUDIO = 0; static const mozilla::TrackID TRACK_VIDEO = 1; -/* We get this callback in order to find out which tracks are audio and which - * are video. We should get this callback right away for existing streams after - * we add this class as a listener. - */ -void -LocalSourceStreamInfo::NotifyQueuedTrackChanges( - mozilla::MediaStreamGraph* aGraph, - mozilla::TrackID aID, - mozilla::TrackRate aTrackRate, - mozilla::TrackTicks aTrackOffset, - uint32_t aTrackEvents, - const mozilla::MediaSegment& aQueuedMedia) -{ - /* TODO: use this callback to keep track of changes to the MediaStream */ -} - /* If the ExpectAudio hint is on we will add a track at the default first * audio track ID (0) * FIX - Do we need to iterate over the tracks instead of taking these hints? @@ -197,13 +181,6 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream localSourceStream->ExpectVideo(TRACK_VIDEO); } - // Make it the listener for info from the MediaStream and add it to the list - mozilla::MediaStream *plainMediaStream = stream->GetStream(); - - if (plainMediaStream) { - plainMediaStream->AddListener(localSourceStream); - } - mLocalSourceStreams.AppendElement(localSourceStream); PR_Unlock(mLocalSourceStreamsLock); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index f46cb8eabf2b..38b76580e379 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -157,7 +157,7 @@ class Fake_VideoGenerator { }; #endif -class LocalSourceStreamInfo : public mozilla::MediaStreamListener { +class LocalSourceStreamInfo { public: LocalSourceStreamInfo(nsDOMMediaStream* aMediaStream) : mMediaStream(aMediaStream) {} @@ -165,24 +165,6 @@ public: mMediaStream = NULL; } - /** - * Notify that changes to one of the stream tracks have been queued. - * aTrackEvents can be any combination of TRACK_EVENT_CREATED and - * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track - * at aTrackOffset (relative to the start of the stream). - */ - virtual void NotifyQueuedTrackChanges( - mozilla::MediaStreamGraph* aGraph, - mozilla::TrackID aID, - mozilla::TrackRate aTrackRate, - mozilla::TrackTicks aTrackOffset, - uint32_t aTrackEvents, - const mozilla::MediaSegment& aQueuedMedia - ); - - virtual void NotifyPull(mozilla::MediaStreamGraph* aGraph, - mozilla::StreamTime aDesiredTime) {} - nsDOMMediaStream* GetMediaStream() { return mMediaStream; } @@ -194,18 +176,16 @@ public: unsigned VideoTrackCount(); void Detach() { - // Disconnect my own listener - GetMediaStream()->GetStream()->RemoveListener(this); - // walk through all the MediaPipelines and disconnect them. for (std::map >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { - it->second->DetachMediaStream(); + it->second->Shutdown(); } mMediaStream = NULL; } + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo) private: std::map > mPipelines; nsRefPtr mMediaStream; @@ -229,7 +209,7 @@ class RemoteSourceStreamInfo { for (std::map >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { - it->second->DetachMediaStream(); + it->second->Shutdown(); } mMediaStream = NULL; } diff --git a/media/webrtc/signaling/test/mediapipeline_unittest.cpp b/media/webrtc/signaling/test/mediapipeline_unittest.cpp index 908efe99f082..522fb5937d91 100644 --- a/media/webrtc/signaling/test/mediapipeline_unittest.cpp +++ b/media/webrtc/signaling/test/mediapipeline_unittest.cpp @@ -96,6 +96,10 @@ class TestAgent { audio_->GetStream()->Stop(); audio_flow_ = NULL; video_flow_ = NULL; + if (audio_pipeline_) + audio_pipeline_->Shutdown(); + if (video_pipeline_) + video_pipeline_->Shutdown(); audio_pipeline_ = NULL; video_pipeline_ = NULL; } @@ -137,9 +141,15 @@ class TestAgentSend : public TestAgent { ConfigureSendMediaCodec(&audio_config_); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - audio_pipeline_ = new mozilla::MediaPipelineTransmit(NULL, - test_utils->sts_target(), - audio_, audio_conduit_, audio_flow_, NULL); + std::string test_pc("PC"); + + audio_pipeline_ = new mozilla::MediaPipelineTransmit( + test_pc, + NULL, + test_utils->sts_target(), + audio_->GetStream(), audio_conduit_, audio_flow_, NULL); + + audio_pipeline_->Init(); // video_ = new Fake_nsDOMMediaStream(new Fake_VideoStreamSource()); // video_pipeline_ = new mozilla::MediaPipelineTransmit(video_, video_conduit_, &video_flow_, &video_flow_); @@ -170,11 +180,16 @@ class TestAgentReceive : public TestAgent { ConfigureRecvMediaCodecs(codecs); EXPECT_EQ(mozilla::kMediaConduitNoError, err); - audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio(NULL, - test_utils->sts_target(), - audio_, - static_cast(audio_conduit_.get()), - audio_flow_, NULL); + std::string test_pc("PC"); + audio_pipeline_ = new mozilla::MediaPipelineReceiveAudio( + test_pc, + NULL, + test_utils->sts_target(), + audio_->GetStream(), + static_cast(audio_conduit_.get()), + audio_flow_, NULL); + + audio_pipeline_->Init(); } private: From 693c7463fa9055715cb765a8ccc9af0cee5e89c6 Mon Sep 17 00:00:00 2001 From: EKR Date: Fri, 21 Dec 2012 07:11:02 -0800 Subject: [PATCH 136/217] Bug 821292 - Clean up runnables when RUN_ON_THREAD runs on the same thread. r=jesup --- media/mtransport/runnable_utils.h | 13 +++++- .../test/runnable_utils_unittest.cpp | 46 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/media/mtransport/runnable_utils.h b/media/mtransport/runnable_utils.h index cfcf213d4720..e45d2ddfe58a 100644 --- a/media/mtransport/runnable_utils.h +++ b/media/mtransport/runnable_utils.h @@ -10,13 +10,13 @@ #define runnable_utils_h__ #include "nsThreadUtils.h" +#include "mozilla/RefPtr.h" // Abstract base class for all of our templates namespace mozilla { class runnable_args_base : public nsRunnable { public: - NS_IMETHOD Run() = 0; }; @@ -39,7 +39,16 @@ class runnable_args_base : public nsRunnable { #include "runnable_utils_generated.h" // Temporary hack. Really we want to have a template which will do this -#define RUN_ON_THREAD(t, r, h) ((t && (t != nsRefPtr(do_GetCurrentThread()))) ? t->Dispatch(r, h) : r->Run()) +static inline nsresult RUN_ON_THREAD(nsIEventTarget *thread, nsIRunnable *runnable, uint32_t flags) { + RefPtr runnable_ref(runnable); + + if (thread && (thread != nsRefPtr(do_GetCurrentThread()))) { + return thread->Dispatch(runnable_ref, flags); + } + + return runnable_ref->Run(); +} + #define ASSERT_ON_THREAD(t) do { \ if (t) { \ bool on; \ diff --git a/media/mtransport/test/runnable_utils_unittest.cpp b/media/mtransport/test/runnable_utils_unittest.cpp index b90273b51a12..e1616cce7135 100644 --- a/media/mtransport/test/runnable_utils_unittest.cpp +++ b/media/mtransport/test/runnable_utils_unittest.cpp @@ -1,4 +1,3 @@ - /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public @@ -15,6 +14,7 @@ #include "nsXPCOM.h" #include "nsXPCOMGlue.h" +#include "mozilla/RefPtr.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsIIOService.h" @@ -37,6 +37,20 @@ MtransportTestUtils *test_utils; namespace { +class Destructor { + public: + Destructor(bool* destroyed) : destroyed_(destroyed) {} + ~Destructor() { + std::cerr << "Destructor called" << std::endl; + *destroyed_ = true; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor); + + private: + bool *destroyed_; +}; + class TargetClass { public: TargetClass(int *ran) : ran_(ran) {} @@ -59,10 +73,16 @@ class TargetClass { std::cerr << __FUNCTION__ << std::endl; return x; } + void destructor_target(Destructor*) { + } + + void destructor_target_ref(RefPtr destructor) { + } int *ran_; }; + class RunnableArgsTest : public ::testing::Test { public: RunnableArgsTest() : ran_(0), cl_(&ran_){} @@ -84,7 +104,6 @@ class RunnableArgsTest : public ::testing::Test { TargetClass cl_; }; - class DispatchTest : public ::testing::Test { public: DispatchTest() : ran_(0), cl_(&ran_) {} @@ -126,8 +145,6 @@ class DispatchTest : public ::testing::Test { protected: int ran_; TargetClass cl_; - - private: nsCOMPtr target_; }; @@ -183,6 +200,27 @@ TEST_F(DispatchTest, TestNonMethodRet) { ASSERT_EQ(10, z); } +TEST_F(DispatchTest, TestDestructor) { + bool destroyed = false; + RefPtr destructor = new Destructor(&destroyed); + target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target, + destructor), + NS_DISPATCH_SYNC); + ASSERT_FALSE(destroyed); + destructor = nullptr; + ASSERT_TRUE(destroyed); +} + +TEST_F(DispatchTest, TestDestructorRef) { + bool destroyed = false; + RefPtr destructor = new Destructor(&destroyed); + target_->Dispatch(WrapRunnable(&cl_, &TargetClass::destructor_target_ref, + destructor), + NS_DISPATCH_SYNC); + ASSERT_FALSE(destroyed); + destructor = nullptr; + ASSERT_TRUE(destroyed); +} } // end of namespace From 28c3dd8d34df5b03b417dc78532399b15a61d872 Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Fri, 21 Dec 2012 12:02:26 -0800 Subject: [PATCH 137/217] Bug 820802 - Disable some telephony tests to try and fix the battery almost perma-oranges, a=test-only --- dom/telephony/test/marionette/manifest.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dom/telephony/test/marionette/manifest.ini b/dom/telephony/test/marionette/manifest.ini index 451697305ff7..a0ba52a74dad 100644 --- a/dom/telephony/test/marionette/manifest.ini +++ b/dom/telephony/test/marionette/manifest.ini @@ -31,9 +31,13 @@ disabled = Bug 806138 [test_outgoing_already_held.js] [test_outgoing_answer_local_hangup.js] [test_outgoing_remote_hangup_held.js] +disabled = Bug 820802 [test_incoming_already_held.js] +disabled = Bug 820802 [test_swap_held_and_active.js] +disabled = Bug 820802 [test_incoming_onstatechange.js] +disabled = Bug 820802 [test_outgoing_onstatechange.js] disabled = Bug 821966 [test_redundant_operations.js] From 060437fe4ed9d0ccfd8ea8656f7f048b7ece2b9e Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Thu, 20 Dec 2012 14:35:03 -0800 Subject: [PATCH 138/217] Bug 817706: Optimize tabs-tray and remote-tabs. [r=mfinkle] --HG-- extra : rebase_source : af830da87f4fbd9d031642eea801a2f41f0e8400 --- mobile/android/base/Makefile.in | 2 - mobile/android/base/RemoteTabs.java | 32 +++++------- mobile/android/base/TabsTray.java | 50 ++++++++----------- .../base/resources/layout/remote_tabs.xml | 13 ----- .../base/resources/layout/tabs_panel.xml.in | 7 ++- .../base/resources/layout/tabs_tray.xml | 11 ---- .../android/base/resources/values/styles.xml | 15 ++++-- 7 files changed, 49 insertions(+), 81 deletions(-) delete mode 100644 mobile/android/base/resources/layout/remote_tabs.xml delete mode 100644 mobile/android/base/resources/layout/tabs_tray.xml diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 07cbe6a2790f..1d737e4c02f4 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -401,14 +401,12 @@ RES_LAYOUT = \ res/layout/setup_screen.xml \ res/layout/shared_ui_components.xml \ res/layout/site_identity_popup.xml \ - res/layout/remote_tabs.xml \ res/layout/remote_tabs_child.xml \ res/layout/remote_tabs_group.xml \ res/layout/tabs_counter.xml \ res/layout/tabs_panel_indicator.xml \ res/layout/tabs_panel_toolbar_menu.xml \ res/layout/tabs_row.xml \ - res/layout/tabs_tray.xml \ res/layout/list_item_header.xml \ res/layout/select_dialog_list.xml \ res/layout/select_dialog_multichoice.xml \ diff --git a/mobile/android/base/RemoteTabs.java b/mobile/android/base/RemoteTabs.java index e810165aaaa2..62e552293ff1 100644 --- a/mobile/android/base/RemoteTabs.java +++ b/mobile/android/base/RemoteTabs.java @@ -15,14 +15,13 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ExpandableListView; -import android.widget.LinearLayout; import android.widget.SimpleExpandableListAdapter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -public class RemoteTabs extends LinearLayout +public class RemoteTabs extends ExpandableListView implements TabsPanel.PanelView, ExpandableListView.OnGroupClickListener, ExpandableListView.OnChildClickListener, @@ -32,8 +31,6 @@ public class RemoteTabs extends LinearLayout private Context mContext; private TabsPanel mTabsPanel; - private static ExpandableListView mList; - private static ArrayList >> mTabsList; private static final String[] CLIENT_KEY = new String[] { "name" }; @@ -45,11 +42,8 @@ public class RemoteTabs extends LinearLayout super(context, attrs); mContext = context; - LayoutInflater.from(context).inflate(R.layout.remote_tabs, this); - - mList = (ExpandableListView) findViewById(R.id.list); - mList.setOnGroupClickListener(this); - mList.setOnChildClickListener(this); + setOnGroupClickListener(this); + setOnChildClickListener(this); } @Override @@ -129,18 +123,18 @@ public class RemoteTabs extends LinearLayout tabsForClient.add(tab); } - mList.setAdapter(new SimpleExpandableListAdapter(mContext, - clients, - R.layout.remote_tabs_group, - CLIENT_KEY, - CLIENT_RESOURCE, - mTabsList, - R.layout.remote_tabs_child, - TAB_KEY, - TAB_RESOURCE)); + setAdapter(new SimpleExpandableListAdapter(mContext, + clients, + R.layout.remote_tabs_group, + CLIENT_KEY, + CLIENT_RESOURCE, + mTabsList, + R.layout.remote_tabs_child, + TAB_KEY, + TAB_RESOURCE)); for (int i = 0; i < clients.size(); i++) { - mList.expandGroup(i); + expandGroup(i); } } } diff --git a/mobile/android/base/TabsTray.java b/mobile/android/base/TabsTray.java index e262fb48a952..85e730fd30d7 100644 --- a/mobile/android/base/TabsTray.java +++ b/mobile/android/base/TabsTray.java @@ -32,14 +32,13 @@ import android.widget.TextView; import java.util.ArrayList; import java.util.List; -public class TabsTray extends LinearLayout +public class TabsTray extends ListView implements TabsPanel.PanelView { private static final String LOGTAG = "GeckoTabsTray"; private Context mContext; private TabsPanel mTabsPanel; - private static ListView mList; private TabsAdapter mTabsAdapter; private List mPendingClosedTabs; @@ -56,26 +55,23 @@ public class TabsTray extends LinearLayout super(context, attrs); mContext = context; - LayoutInflater.from(context).inflate(R.layout.tabs_tray, this); - mCloseAnimationCount = 0; mPendingClosedTabs = new ArrayList(); - mList = (ListView) findViewById(R.id.list); - mList.setItemsCanFocus(true); + setItemsCanFocus(true); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray); boolean isPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1); a.recycle(); mTabsAdapter = new TabsAdapter(mContext, isPrivate); - mList.setAdapter(mTabsAdapter); + setAdapter(mTabsAdapter); - mSwipeListener = new TabSwipeGestureListener(mList); - mList.setOnTouchListener(mSwipeListener); - mList.setOnScrollListener(mSwipeListener.makeScrollListener()); + mSwipeListener = new TabSwipeGestureListener(); + setOnTouchListener(mSwipeListener); + setOnScrollListener(mSwipeListener.makeScrollListener()); - mList.setRecyclerListener(new RecyclerListener() { + setRecyclerListener(new RecyclerListener() { @Override public void onMovedToScrapHeap(View view) { TabRow row = (TabRow) view.getTag(); @@ -167,7 +163,7 @@ public class TabsTray extends LinearLayout // We just need to update the style for the unselected tab... case THUMBNAIL: case TITLE: - View view = mList.getChildAt(getPositionForTab(tab) - mList.getFirstVisiblePosition()); + View view = TabsTray.this.getChildAt(getPositionForTab(tab) - TabsTray.this.getFirstVisiblePosition()); if (view == null) return; @@ -198,7 +194,7 @@ public class TabsTray extends LinearLayout if (selected == -1) return; - mList.setSelection(selected); + TabsTray.this.setSelection(selected); } public void clear() { @@ -361,7 +357,6 @@ public class TabsTray extends LinearLayout private int mMaxFlingVelocity; private VelocityTracker mVelocityTracker; - private ListView mListView; private int mListWidth = 1; private View mSwipeView; @@ -373,16 +368,14 @@ public class TabsTray extends LinearLayout private boolean mSwiping; private boolean mEnabled; - public TabSwipeGestureListener(ListView listView) { - mListView = listView; - + public TabSwipeGestureListener() { mSwipeView = null; mSwipeProxy = null; mSwipeViewPosition = ListView.INVALID_POSITION; mSwiping = false; mEnabled = true; - ViewConfiguration vc = ViewConfiguration.get(listView.getContext()); + ViewConfiguration vc = ViewConfiguration.get(TabsTray.this.getContext()); mSwipeThreshold = vc.getScaledTouchSlop(); mMinFlingVelocity = (int) (getContext().getResources().getDisplayMetrics().density * MIN_VELOCITY); mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); @@ -411,7 +404,7 @@ public class TabsTray extends LinearLayout return false; if (mListWidth < 2) - mListWidth = mListView.getWidth(); + mListWidth = TabsTray.this.getWidth(); switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: { @@ -424,7 +417,7 @@ public class TabsTray extends LinearLayout if (mSwipeView != null) { mSwipeStart = e.getRawX(); - mSwipeViewPosition = mListView.getPositionForView(mSwipeView); + mSwipeViewPosition = TabsTray.this.getPositionForView(mSwipeView); mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(e); @@ -497,7 +490,7 @@ public class TabsTray extends LinearLayout cancelCheckForTap(); mSwiping = true; - mListView.requestDisallowInterceptTouchEvent(true); + TabsTray.this.requestDisallowInterceptTouchEvent(true); TabRow tab = (TabRow) mSwipeView.getTag(); tab.close.setVisibility(View.INVISIBLE); @@ -507,7 +500,7 @@ public class TabsTray extends LinearLayout MotionEvent cancelEvent = MotionEvent.obtain(e); cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (e.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT)); - mListView.onTouchEvent(cancelEvent); + TabsTray.this.onTouchEvent(cancelEvent); mSwipeProxy = AnimatorProxy.create(mSwipeView); } @@ -528,19 +521,16 @@ public class TabsTray extends LinearLayout } private View findViewAt(float rawX, float rawY) { - if (mList == null) - return null; - Rect rect = new Rect(); int[] listViewCoords = new int[2]; - mListView.getLocationOnScreen(listViewCoords); + TabsTray.this.getLocationOnScreen(listViewCoords); int x = (int) rawX - listViewCoords[0]; int y = (int) rawY - listViewCoords[1]; - for (int i = 0; i < mListView.getChildCount(); i++) { - View child = mListView.getChildAt(i); + for (int i = 0; i < TabsTray.this.getChildCount(); i++) { + View child = TabsTray.this.getChildAt(i); child.getHitRect(rect); if (rect.contains(x, y)) @@ -554,14 +544,14 @@ public class TabsTray extends LinearLayout if (mPendingCheckForTap == null) mPendingCheckForTap = new CheckForTap(); - mListView.postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); + TabsTray.this.postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } private void cancelCheckForTap() { if (mPendingCheckForTap == null) return; - mListView.removeCallbacks(mPendingCheckForTap); + TabsTray.this.removeCallbacks(mPendingCheckForTap); } private class CheckForTap implements Runnable { diff --git a/mobile/android/base/resources/layout/remote_tabs.xml b/mobile/android/base/resources/layout/remote_tabs.xml deleted file mode 100644 index 7d3b3a368dc7..000000000000 --- a/mobile/android/base/resources/layout/remote_tabs.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - diff --git a/mobile/android/base/resources/layout/tabs_panel.xml.in b/mobile/android/base/resources/layout/tabs_panel.xml.in index 5d4a9fe414c2..67acdbc9173a 100644 --- a/mobile/android/base/resources/layout/tabs_panel.xml.in +++ b/mobile/android/base/resources/layout/tabs_panel.xml.in @@ -26,18 +26,21 @@ android:layout_height="wrap_content"> + android:layout_height="fill_parent"/> diff --git a/mobile/android/base/resources/layout/tabs_tray.xml b/mobile/android/base/resources/layout/tabs_tray.xml deleted file mode 100644 index 94b565886f6d..000000000000 --- a/mobile/android/base/resources/layout/tabs_tray.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/mobile/android/base/resources/values/styles.xml b/mobile/android/base/resources/values/styles.xml index 8d3990195945..c45188cf3121 100644 --- a/mobile/android/base/resources/values/styles.xml +++ b/mobile/android/base/resources/values/styles.xml @@ -135,11 +135,18 @@ 20dp - + + + + From 840ced9d8a8f18483b6b761c3e28f66291f7b912 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Thu, 20 Dec 2012 16:49:51 -0800 Subject: [PATCH 139/217] Bug 823781: Compound drawables for promo-box. [r=mfinkle] --HG-- extra : rebase_source : 0af2667d01ad8f16b68a1ba0af3710fa07d5fa93 --- mobile/android/base/AboutHomePromoBox.java | 16 ++++--------- mobile/android/base/Makefile.in | 1 - .../abouthome_content.xml.in | 4 +++- .../resources/layout/abouthome_content.xml.in | 4 +++- .../resources/layout/abouthome_promo_box.xml | 24 ------------------- 5 files changed, 10 insertions(+), 39 deletions(-) delete mode 100644 mobile/android/base/resources/layout/abouthome_promo_box.xml diff --git a/mobile/android/base/AboutHomePromoBox.java b/mobile/android/base/AboutHomePromoBox.java index e0b673c54b01..a882458a56e3 100644 --- a/mobile/android/base/AboutHomePromoBox.java +++ b/mobile/android/base/AboutHomePromoBox.java @@ -18,7 +18,6 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; /** @@ -27,7 +26,7 @@ import android.widget.TextView; * To do this, add a new Type value and update show() to call setResources() for your values - * including a set[Box Type]Resources() helper method is recommended. */ -public class AboutHomePromoBox extends LinearLayout implements View.OnClickListener { +public class AboutHomePromoBox extends TextView implements View.OnClickListener { private static final String LOGTAG = "AboutHomePromoBox"; public enum Type { NONE, SYNC, APPS }; @@ -35,8 +34,6 @@ public class AboutHomePromoBox extends LinearLayout implements View.OnClickListe private Type mType; private final Context mContext; - private final TextView mTextView; - private final ImageView mImageView; // Use setResources() to set these variables for each PromoBox type. private int mTextResource; @@ -46,12 +43,7 @@ public class AboutHomePromoBox extends LinearLayout implements View.OnClickListe public AboutHomePromoBox(Context context, AttributeSet attrs) { super(context, attrs); - final LayoutInflater inflater = LayoutInflater.from(context); - inflater.inflate(R.layout.abouthome_promo_box, this); - mContext = context; - mTextView = (TextView) findViewById(R.id.text); - mImageView = (ImageView) findViewById(R.id.icon); setOnClickListener(this); } @@ -111,7 +103,7 @@ public class AboutHomePromoBox extends LinearLayout implements View.OnClickListe private void updateViewResources() { updateTextViewResources(); - mImageView.setImageResource(mImageResource); + setCompoundDrawablesWithIntrinsicBounds(mImageResource, 0, 0, 0); } private void updateTextViewResources() { @@ -119,12 +111,12 @@ public class AboutHomePromoBox extends LinearLayout implements View.OnClickListe final String boldName = mContext.getResources().getString(mBoldTextResource); final int styleIndex = promoText.indexOf(boldName); if (styleIndex < 0) - mTextView.setText(promoText); + setText(promoText); else { final SpannableString spannableText = new SpannableString(promoText); spannableText.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), styleIndex, styleIndex + boldName.length(), 0); - mTextView.setText(spannableText, TextView.BufferType.SPANNABLE); + setText(spannableText, TextView.BufferType.SPANNABLE); } } diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 1d737e4c02f4..d08f60498c61 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -412,7 +412,6 @@ RES_LAYOUT = \ res/layout/select_dialog_multichoice.xml \ res/layout/abouthome_addon_row.xml \ res/layout/abouthome_last_tabs_row.xml \ - res/layout/abouthome_promo_box.xml \ res/layout/abouthome_section.xml \ res/layout/abouthome_remote_tab_row.xml \ res/layout/abouthome_topsite_item.xml \ diff --git a/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in b/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in index e3866f882470..b35214b69122 100644 --- a/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in +++ b/mobile/android/base/resources/layout-xlarge-land-v11/abouthome_content.xml.in @@ -42,7 +42,6 @@ style="@style/AboutHome.Thumbnail.Grid"/> diff --git a/mobile/android/base/resources/layout/abouthome_content.xml.in b/mobile/android/base/resources/layout/abouthome_content.xml.in index 46525a1ee834..5f1be21f07c0 100644 --- a/mobile/android/base/resources/layout/abouthome_content.xml.in +++ b/mobile/android/base/resources/layout/abouthome_content.xml.in @@ -52,7 +52,6 @@ style="@style/AboutHome.Thumbnail.Grid"/> - - - - - - - - - From 8602b900f7d2aef271ecd1a72bd1b28258266e58 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 21 Dec 2012 12:14:47 -0800 Subject: [PATCH 140/217] Bug 823953: Improve sync queue handling. r=bent --HG-- extra : rebase_source : 04c9eb2a7462d2c94c66b8126507fa690df0f13d --- dom/workers/RuntimeService.cpp | 5 +++-- dom/workers/ScriptLoader.cpp | 8 +++---- dom/workers/WorkerPrivate.cpp | 10 ++++++++- dom/workers/WorkerPrivate.h | 39 ++++++++++++++++++++++++++++++++++ dom/workers/XMLHttpRequest.cpp | 32 +++++++++++++++++----------- 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 347a2d45fb76..43b5846f0cdc 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -306,14 +306,15 @@ public: bool Dispatch(JSContext* aCx) { - mSyncQueueKey = mWorkerPrivate->CreateNewSyncLoop(); + AutoSyncLoopHolder syncLoop(mWorkerPrivate); + mSyncQueueKey = syncLoop.SyncQueueKey(); if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { JS_ReportError(aCx, "Failed to dispatch to main thread!"); return false; } - return mWorkerPrivate->RunSyncLoop(aCx, mSyncQueueKey); + return syncLoop.RunAndForget(aCx); } NS_IMETHOD diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp index 0be3780da2d5..75ddf6d68f0c 100644 --- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -666,11 +666,11 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate, aWorkerPrivate->AssertIsOnWorkerThread(); NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!"); - uint32_t syncQueueKey = aWorkerPrivate->CreateNewSyncLoop(); + AutoSyncLoopHolder syncLoop(aWorkerPrivate); nsRefPtr loader = - new ScriptLoaderRunnable(aWorkerPrivate, syncQueueKey, aLoadInfos, - aIsWorkerScript); + new ScriptLoaderRunnable(aWorkerPrivate, syncLoop.SyncQueueKey(), + aLoadInfos, aIsWorkerScript); NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!"); @@ -685,7 +685,7 @@ LoadAllScripts(JSContext* aCx, WorkerPrivate* aWorkerPrivate, return false; } - return aWorkerPrivate->RunSyncLoop(aCx, syncQueueKey); + return syncLoop.RunAndForget(aCx); } } /* anonymous namespace */ diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 08bd14f55b0b..725bbd85a766 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3390,7 +3390,7 @@ WorkerPrivate::RunSyncLoop(JSContext* aCx, uint32_t aSyncLoopKey) NS_ASSERTION(syncQueue->mQueue.IsEmpty(), "Unprocessed sync events!"); bool result = syncQueue->mResult; - mSyncQueues.RemoveElementAt(aSyncLoopKey); + DestroySyncLoop(aSyncLoopKey); #ifdef DEBUG syncQueue = nullptr; @@ -3424,6 +3424,14 @@ WorkerPrivate::StopSyncLoop(uint32_t aSyncLoopKey, bool aSyncResult) syncQueue->mComplete = true; } +void +WorkerPrivate::DestroySyncLoop(uint32_t aSyncLoopKey) +{ + AssertIsOnWorkerThread(); + + mSyncQueues.RemoveElementAt(aSyncLoopKey); +} + bool WorkerPrivate::PostMessageToParent(JSContext* aCx, jsval aMessage, jsval aTransferable) diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index bb2bb8a7ff5a..cd6753ecdbdc 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -661,6 +661,9 @@ public: void StopSyncLoop(uint32_t aSyncLoopKey, bool aSyncResult); + void + DestroySyncLoop(uint32_t aSyncLoopKey); + bool PostMessageToParent(JSContext* aCx, jsval aMessage, jsval transferable); @@ -848,6 +851,42 @@ WorkerStructuredCloneCallbacks(bool aMainRuntime); JSStructuredCloneCallbacks* ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime); +class AutoSyncLoopHolder +{ +public: + AutoSyncLoopHolder(WorkerPrivate* aWorkerPrivate) + : mWorkerPrivate(aWorkerPrivate), mSyncLoopKey(UINT32_MAX) + { + mSyncLoopKey = mWorkerPrivate->CreateNewSyncLoop(); + } + + ~AutoSyncLoopHolder() + { + if (mWorkerPrivate) { + mWorkerPrivate->StopSyncLoop(mSyncLoopKey, false); + mWorkerPrivate->DestroySyncLoop(mSyncLoopKey); + } + } + + bool + RunAndForget(JSContext* aCx) + { + WorkerPrivate* workerPrivate = mWorkerPrivate; + mWorkerPrivate = nullptr; + return workerPrivate->RunSyncLoop(aCx, mSyncLoopKey); + } + + uint32_t + SyncQueueKey() const + { + return mSyncLoopKey; + } + +private: + WorkerPrivate* mWorkerPrivate; + uint32_t mSyncLoopKey; +}; + END_WORKERS_NAMESPACE #endif /* mozilla_dom_workers_workerprivate_h__ */ diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index 505a2bbb113f..1d560be523a7 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -830,18 +830,15 @@ public: { mWorkerPrivate->AssertIsOnWorkerThread(); - mSyncQueueKey = mWorkerPrivate->CreateNewSyncLoop(); + AutoSyncLoopHolder syncLoop(mWorkerPrivate); + mSyncQueueKey = syncLoop.SyncQueueKey(); if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { JS_ReportError(aCx, "Failed to dispatch to main thread!"); return false; } - if (!mWorkerPrivate->RunSyncLoop(aCx, mSyncQueueKey)) { - return false; - } - - return true; + return syncLoop.RunAndForget(aCx); } virtual nsresult @@ -1707,10 +1704,13 @@ XMLHttpRequest::SendInternal(const nsAString& aStringBody, } AutoUnpinXHR autoUnpin(this); + Maybe autoSyncLoop; uint32_t syncQueueKey = UINT32_MAX; - if (mProxy->mIsSyncXHR) { - syncQueueKey = mWorkerPrivate->CreateNewSyncLoop(); + bool isSyncXHR = mProxy->mIsSyncXHR; + if (isSyncXHR) { + autoSyncLoop.construct(mWorkerPrivate); + syncQueueKey = autoSyncLoop.ref().SyncQueueKey(); } mProxy->mOuterChannelId++; @@ -1724,16 +1724,24 @@ XMLHttpRequest::SendInternal(const nsAString& aStringBody, return; } - autoUnpin.Clear(); + if (!isSyncXHR) { + autoUnpin.Clear(); + MOZ_ASSERT(autoSyncLoop.empty()); + return; + } - // The event loop was spun above, make sure we aren't canceled already. + // If our sync XHR was canceled during the send call the worker is going + // away. We have no idea how far through the send call we got. There may + // be a ProxyCompleteRunnable in the sync loop, but rather than run the loop + // to get it we just let our RAII helpers clean up. if (mCanceled) { return; } - if (mProxy->mIsSyncXHR && !mWorkerPrivate->RunSyncLoop(cx, syncQueueKey)) { + autoUnpin.Clear(); + + if (!autoSyncLoop.ref().RunAndForget(cx)) { aRv.Throw(NS_ERROR_FAILURE); - return; } } From 2b5cf2df81b2c011809d45fd0b99fb04b930403a Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Fri, 21 Dec 2012 15:50:49 -0500 Subject: [PATCH 141/217] Bug 820302 - Don't abort the pan-zoom animation on a viewport update if the page size didn't really change. r=Cwiiis --- mobile/android/base/gfx/GeckoLayerClient.java | 4 +++- mobile/android/base/gfx/ImmutableViewportMetrics.java | 9 ++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mobile/android/base/gfx/GeckoLayerClient.java b/mobile/android/base/gfx/GeckoLayerClient.java index 80fb81e5c1c3..7a711bc1aaa8 100644 --- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -302,7 +302,9 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget case UPDATE: // Keep the old viewport size metrics = messageMetrics.setViewportSize(oldMetrics.getWidth(), oldMetrics.getHeight()); - abortPanZoomAnimation(); + if (!oldMetrics.fuzzyEquals(metrics)) { + abortPanZoomAnimation(); + } break; case PAGE_SIZE: // adjust the page dimensions to account for differences in zoom diff --git a/mobile/android/base/gfx/ImmutableViewportMetrics.java b/mobile/android/base/gfx/ImmutableViewportMetrics.java index 4ca8515a8f43..b6d52b2ab8ad 100644 --- a/mobile/android/base/gfx/ImmutableViewportMetrics.java +++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java @@ -211,11 +211,10 @@ public class ImmutableViewportMetrics { } public boolean fuzzyEquals(ImmutableViewportMetrics other) { - return FloatUtils.fuzzyEquals(pageRectLeft, other.pageRectLeft) - && FloatUtils.fuzzyEquals(pageRectTop, other.pageRectTop) - && FloatUtils.fuzzyEquals(pageRectRight, other.pageRectRight) - && FloatUtils.fuzzyEquals(pageRectBottom, other.pageRectBottom) - && FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) + // Don't bother checking the pageRectXXX values because they are a product + // of the cssPageRectXXX values and the zoomFactor, except with more rounding + // error. Checking those is both inefficient and can lead to false negatives. + return FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft) && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop) && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight) && FloatUtils.fuzzyEquals(cssPageRectBottom, other.cssPageRectBottom) From 446803387bbd59243f8b63fcf179c650f9922752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Fri, 21 Dec 2012 22:05:45 +0100 Subject: [PATCH 142/217] Bug 802421 - Allow specifying a mic and a camera in the doorhanger UI when audio and video are both requested through gUM. r=gavin --- browser/base/content/browser.xul | 21 ++++++ .../locales/en-US/chrome/browser/browser.dtd | 5 ++ .../en-US/chrome/browser/browser.properties | 16 ++--- browser/modules/webrtcUI.jsm | 67 +++++++++++-------- 4 files changed, 73 insertions(+), 36 deletions(-) diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 265ecf3520d4..b8e86d9960da 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -443,6 +443,27 @@ #endif + + #ifdef CAN_DRAW_IN_TITLEBAR diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 0e9995c6f697..46770d8f62c0 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -623,3 +623,8 @@ just addresses the organization to follow, e.g. "This site is run by " --> + + + + + diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index b979e2c8c0d0..b17d7662c5fe 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -442,17 +442,15 @@ identity.loggedIn.signOut.label = Sign Out identity.loggedIn.signOut.accessKey = O # LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org) -# LOCALIZATION NOTE (getUserMedia.shareMicrophone.message, getUserMedia.shareSpecificMicrophone.label): %S is the website origin (e.g. www.mozilla.org) +# LOCALIZATION NOTE (getUserMedia.shareMicrophone.message): %S is the website origin (e.g. www.mozilla.org) +# LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label): +# Semi-colon list of plural forms. See: +# http://developer.mozilla.org/en/docs/Localization_and_Plurals +# The number of devices can be either one or two. getUserMedia.shareCamera.message = Would you like to share your camera with %S? -getUserMedia.shareCamera.label = Share Camera -getUserMedia.shareCamera.accesskey = S -getUserMedia.shareSpecificCamera.label = Share Camera: %S getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S? -getUserMedia.shareMicrophone.label = Share Microphone -getUserMedia.shareMicrophone.accesskey = S -getUserMedia.shareSpecificMicrophone.label = Share Microphone: %S getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S? -getUserMedia.shareCameraAndMicrophone.label = Share Camera and Microphone -getUserMedia.shareCameraAndMicrophone.accesskey = S +getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices +getUserMedia.shareSelectedDevices.accesskey = S getUserMedia.denyRequest.label = Don't Share getUserMedia.denyRequest.accesskey = D diff --git a/browser/modules/webrtcUI.jsm b/browser/modules/webrtcUI.jsm index f385c4cbe045..1fe4537e8213 100644 --- a/browser/modules/webrtcUI.jsm +++ b/browser/modules/webrtcUI.jsm @@ -7,9 +7,11 @@ this.EXPORTED_SYMBOLS = ["webrtcUI"]; const Cu = Components.utils; +const Cc = Components.classes; const Ci = Components.interfaces; Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/PluralForm.jsm"); this.webrtcUI = { init: function () { @@ -72,47 +74,58 @@ function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) { return; let host = aBrowser.contentDocument.documentURIObject.asciiHost; - let chromeWin = aBrowser.ownerDocument.defaultView; + let chromeDoc = aBrowser.ownerDocument; + let chromeWin = chromeDoc.defaultView; let stringBundle = chromeWin.gNavigatorBundle; let message = stringBundle.getFormattedString("getUserMedia." + requestType + ".message", [ host ]); + function listDevices(menupopup, devices) { + while (menupopup.lastChild) + menupopup.removeChild(menupopup.lastChild); + + let deviceIndex = 0; + for (let device of devices) { + let menuitem = chromeDoc.createElement("menuitem"); + menuitem.setAttribute("value", deviceIndex); + menuitem.setAttribute("label", device.name); + menuitem.setAttribute("tooltiptext", device.name); + menupopup.appendChild(menuitem); + deviceIndex++; + } + } + + chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length; + chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length; + listDevices(chromeDoc.getElementById("webRTC-selectCamera-menupopup"), videoDevices); + listDevices(chromeDoc.getElementById("webRTC-selectMicrophone-menupopup"), audioDevices); + let mainAction = { - label: stringBundle.getString("getUserMedia." + requestType + ".label"), - accessKey: stringBundle.getString("getUserMedia." + requestType + ".accesskey"), + label: PluralForm.get(requestType == "shareCameraAndMicrophone" ? 2 : 1, + stringBundle.getString("getUserMedia.shareSelectedDevices.label")), + accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"), callback: function () { - Services.obs.notifyObservers(null, "getUserMedia:response:allow", aCallID); + let allowedDevices = Cc["@mozilla.org/supports-array;1"] + .createInstance(Ci.nsISupportsArray); + if (videoDevices.length) { + let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value; + allowedDevices.AppendElement(videoDevices[videoDeviceIndex]); + } + if (audioDevices.length) { + let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value; + allowedDevices.AppendElement(audioDevices[audioDeviceIndex]); + } + Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID); } }; - let secondaryActions = []; - let selectableDevices = videoDevices.length ? videoDevices : audioDevices; - if (selectableDevices.length > 1) { - let selectableDeviceNumber = 0; - for (let device of selectableDevices) { - // See bug 449811 for why we do this - let actual_device = device; - selectableDeviceNumber++; - secondaryActions.push({ - label: stringBundle.getFormattedString( - device.type == "audio" ? - "getUserMedia.shareSpecificMicrophone.label" : - "getUserMedia.shareSpecificCamera.label", - [ device.name ]), - accessKey: selectableDeviceNumber, - callback: function () { - Services.obs.notifyObservers(actual_device, "getUserMedia:response:allow", aCallID); - } - }); - } - } - secondaryActions.push({ + let secondaryActions = [{ label: stringBundle.getString("getUserMedia.denyRequest.label"), accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"), callback: function () { Services.obs.notifyObservers(null, "getUserMedia:response:deny", aCallID); } - }); + }]; let options = { }; From 22e9dc9724aa121399dcd26664b1a2f8d21289ce Mon Sep 17 00:00:00 2001 From: Randell Jesup Date: Thu, 20 Dec 2012 12:03:07 -0500 Subject: [PATCH 143/217] Bug 823453 - Change gUM permissions response to be an array. r=anant --- dom/media/MediaManager.cpp | 41 ++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 1ddb606f6300..79a2c3782079 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -995,20 +995,35 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic, mActiveCallbacks.Remove(key); if (aSubject) { - // A particular device was chosen by the user. + // A particular device or devices were chosen by the user. // NOTE: does not allow setting a device to null; assumes nullptr - nsCOMPtr device = do_QueryInterface(aSubject); - if (device) { - GetUserMediaRunnable* gUMRunnable = - static_cast(runnable.get()); - nsString type; - device->GetType(type); - if (type.EqualsLiteral("video")) { - gUMRunnable->SetVideoDevice(static_cast(device.get())); - } else if (type.EqualsLiteral("audio")) { - gUMRunnable->SetAudioDevice(static_cast(device.get())); - } else { - NS_WARNING("Unknown device type in getUserMedia"); + GetUserMediaRunnable* gUMRunnable = + static_cast(runnable.get()); + + nsCOMPtr array(do_QueryInterface(aSubject)); + MOZ_ASSERT(array); + uint32_t len = 0; + array->Count(&len); + MOZ_ASSERT(len); + if (!len) { + gUMRunnable->Denied(); // neither audio nor video were selected + return NS_OK; + } + for (uint32_t i = 0; i < len; i++) { + nsCOMPtr supports; + array->GetElementAt(i,getter_AddRefs(supports)); + nsCOMPtr device(do_QueryInterface(supports)); + MOZ_ASSERT(device); // shouldn't be returning anything else... + if (device) { + nsString type; + device->GetType(type); + if (type.EqualsLiteral("video")) { + gUMRunnable->SetVideoDevice(static_cast(device.get())); + } else if (type.EqualsLiteral("audio")) { + gUMRunnable->SetAudioDevice(static_cast(device.get())); + } else { + NS_WARNING("Unknown device type in getUserMedia"); + } } } } From 0900576d4c2f89f274bb8e98dd073e4712d6ac94 Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Fri, 21 Dec 2012 21:40:06 +0000 Subject: [PATCH 144/217] Bug 806432 - When creating the offscreen surface into which group opacity, mask or non-trivial-clipPath are painted, use the non-transformed visual overflow rect. r=roc. --- .../svg/clipPath-and-transform-01.svg | 23 +++++++++++++++++++ layout/reftests/svg/mask-transformed-02.svg | 19 +++++++++++++++ .../svg/opacity-and-transform-01-ref.svg | 7 ++++++ .../reftests/svg/opacity-and-transform-01.svg | 22 ++++++++++++++++++ layout/reftests/svg/reftest.list | 3 +++ layout/svg/nsSVGIntegrationUtils.cpp | 3 ++- 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/svg/clipPath-and-transform-01.svg create mode 100644 layout/reftests/svg/mask-transformed-02.svg create mode 100644 layout/reftests/svg/opacity-and-transform-01-ref.svg create mode 100644 layout/reftests/svg/opacity-and-transform-01.svg diff --git a/layout/reftests/svg/clipPath-and-transform-01.svg b/layout/reftests/svg/clipPath-and-transform-01.svg new file mode 100644 index 000000000000..6d128a01f5ef --- /dev/null +++ b/layout/reftests/svg/clipPath-and-transform-01.svg @@ -0,0 +1,23 @@ + + + + Testcase for clip-path on elements that are transformed + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/mask-transformed-02.svg b/layout/reftests/svg/mask-transformed-02.svg new file mode 100644 index 000000000000..4361895d8642 --- /dev/null +++ b/layout/reftests/svg/mask-transformed-02.svg @@ -0,0 +1,19 @@ + + + + Testcase for mask on elements that are transformed + + + + + + + + + + + + diff --git a/layout/reftests/svg/opacity-and-transform-01-ref.svg b/layout/reftests/svg/opacity-and-transform-01-ref.svg new file mode 100644 index 000000000000..9123b3255ff7 --- /dev/null +++ b/layout/reftests/svg/opacity-and-transform-01-ref.svg @@ -0,0 +1,7 @@ + + + + diff --git a/layout/reftests/svg/opacity-and-transform-01.svg b/layout/reftests/svg/opacity-and-transform-01.svg new file mode 100644 index 000000000000..74cd984e0016 --- /dev/null +++ b/layout/reftests/svg/opacity-and-transform-01.svg @@ -0,0 +1,22 @@ + + + + Testcase for opacity on elements that are transformed + + + + + + + + + + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index cf9580147c81..90f7ae7a76f5 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -30,6 +30,7 @@ include svg-integration/reftest.list == clip-02b.svg clip-02-ref.svg == clipPath-advanced-01.svg pass.svg fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),1,2) == clipPath-and-shape-rendering-01.svg clipPath-and-shape-rendering-01-ref.svg # bug 614840 +== clipPath-and-transform-01.svg pass.svg == clipPath-basic-01.svg pass.svg == clipPath-basic-02.svg pass.svg == clipPath-basic-03.svg pass.svg @@ -177,6 +178,7 @@ fuzzy-if(Android,9,980) == gradient-live-01d.svg gradient-live-01-ref.svg == mask-extref-dataURI-01.svg pass.svg == mask-containing-masked-content-01.svg pass.svg == mask-transformed-01.svg mask-transformed-01-ref.svg +== mask-transformed-02.svg pass.svg pref(layout.css.masking.enabled,true) == mask-type-01.svg mask-type-01-ref.svg pref(layout.css.masking.enabled,true) == mask-type-02.svg mask-type-01-ref.svg pref(layout.css.masking.enabled,true) == mask-type-03.svg mask-type-01-ref.svg @@ -199,6 +201,7 @@ random-if(gtk2Widget) == objectBoundingBox-and-fePointLight-02.svg objectBoundin == opacity-and-gradient-01.svg pass.svg == opacity-and-gradient-02.svg opacity-and-gradient-02-ref.svg == opacity-and-pattern-01.svg pass.svg +== opacity-and-transform-01.svg opacity-and-transform-01-ref.svg == outer-svg-border-and-padding-01.svg outer-svg-border-and-padding-01-ref.svg == path-01.svg path-01-ref.svg == path-02.svg pass.svg diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index e94fac9094f9..b000634b1a60 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -477,7 +477,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx, if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) { complexEffects = true; gfx->Save(); - aCtx->IntersectClip(aFrame->GetVisualOverflowRect() + svgGeomFramePos); + aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() + + svgGeomFramePos); gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); } From 9da238bbcdacf79f9cbdd1859870a117b1639635 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 21 Dec 2012 16:57:05 -0500 Subject: [PATCH 145/217] Bug 790054 - Don't offer to connect to WPA-EAP access points. Continue to allow connecting to already-configured access points. r=vchang --- dom/wifi/WifiWorker.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index fccccf69d5f1..a0b506771990 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -2046,9 +2046,9 @@ function WifiWorker() { // results, add it to the list along with any other information. // Also, we use the highest signal strength that we see. let network = new ScanResult(ssid, bssid, flags, signalLevel); - self.networksArray.push(network); let networkKey = getNetworkKey(network); + let eapIndex = -1; if (networkKey in self.configuredNetworks) { let known = self.configuredNetworks[networkKey]; network.known = true; @@ -2063,8 +2063,20 @@ function WifiWorker() { ("wep_key0" in known && known.wep_key0)) { network.password = "*"; } + } else if ((eapIndex = network.capabilities.indexOf("WPA-EAP")) >= 0) { + // Don't offer to connect to WPA-EAP networks unless one has been + // configured through other means (e.g. it was added directly to + // wpa_supplicant.conf). Here, we have an unknown WPA-EAP network, + // so we ignore it entirely if it only supports WPA-EAP, otherwise + // we take EAP out of the list and offer the rest of the + // capabilities. + if (network.capabilities.length === 1) + continue; + + network.capabilities.splice(eapIndex, 1); } + self.networksArray.push(network); if (network.bssid === WifiManager.connectionInfo.bssid) network.connected = true; From f61865728497cd02be98dd501d2a7e3f9318f057 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Fri, 21 Dec 2012 16:57:19 -0500 Subject: [PATCH 146/217] Bug 790054 - Allow WPA-EAP networks based on a gecko pref. r=fabrice --- dom/wifi/WifiWorker.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index a0b506771990..63b00218449f 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -1619,6 +1619,7 @@ function WifiWorker() { this.wantScanResults = []; + this._allowWpaEap = false; this._needToEnableNetworks = false; this._highestPriority = -1; @@ -1785,6 +1786,12 @@ function WifiWorker() { WifiManager.saveConfig(function() {}) }); + try { + self._allowWpaEap = Services.prefs.getBoolPref("b2g.wifi.allow_unsafe_wpa_eap"); + } catch (e) { + self._allowWpaEap = false; + } + // Check if we need to dequeue requests first. self._notifyAfterStateChange(true, true); @@ -2063,7 +2070,8 @@ function WifiWorker() { ("wep_key0" in known && known.wep_key0)) { network.password = "*"; } - } else if ((eapIndex = network.capabilities.indexOf("WPA-EAP")) >= 0) { + } else if (!self._allowWpaEap && + (eapIndex = network.capabilities.indexOf("WPA-EAP")) >= 0) { // Don't offer to connect to WPA-EAP networks unless one has been // configured through other means (e.g. it was added directly to // wpa_supplicant.conf). Here, we have an unknown WPA-EAP network, From 35b1d042046e38462f6aa8e12bfca40ccab425b1 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Fri, 21 Dec 2012 14:03:33 -0800 Subject: [PATCH 147/217] Bug 819856, Re-enabled WebRTC voice engine code for Android, r=jesup --- .../trunk/src/voice_engine/voice_engine_defines.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/media/webrtc/trunk/src/voice_engine/voice_engine_defines.h b/media/webrtc/trunk/src/voice_engine/voice_engine_defines.h index 7d4c7299cdcd..633f29ba29e9 100644 --- a/media/webrtc/trunk/src/voice_engine/voice_engine_defines.h +++ b/media/webrtc/trunk/src/voice_engine/voice_engine_defines.h @@ -386,7 +386,10 @@ namespace webrtc // Always excluded for Android builds #undef WEBRTC_CODEC_ISAC - #undef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT + // We need WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT to make things work on Android. + // Motivation for the commented-out undef below is unclear. + // + // #undef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT #undef WEBRTC_CONFERENCING #undef WEBRTC_TYPING_DETECTION @@ -406,10 +409,10 @@ namespace webrtc #define WEBRTC_VOICE_ENGINE_AGC_DEFAULT_MODE \ GainControl::kAdaptiveDigital - #define ANDROID_NOT_SUPPORTED(stat) \ - stat.SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, \ - "API call not supported"); \ - return -1; + // This macro used to cause the calling function to set an error code and return. + // However, not doing that seems to cause the unit tests to pass / behave reasonably, + // so it's disabled for now; see bug 819856. + #define ANDROID_NOT_SUPPORTED(stat) #else // LINUX PC // ---------------------------------------------------------------------------- From de026650c94d4250f12e430fabb8ffa80ce1c05c Mon Sep 17 00:00:00 2001 From: William Chen Date: Thu, 1 Nov 2012 11:18:08 -0700 Subject: [PATCH 148/217] Bug 783129 - Implementation of document.register without shadow DOM support. r=mrbkap, bent --- content/base/src/nsDocument.cpp | 253 ++++++++++++++++++ content/base/src/nsDocument.h | 20 ++ content/base/src/nsNodeUtils.h | 10 + content/events/src/nsEventDispatcher.cpp | 2 + dom/base/nsDOMClassInfo.cpp | 39 +++ dom/base/nsDOMClassInfo.h | 18 ++ dom/interfaces/core/nsIDOMDocument.idl | 28 +- dom/interfaces/events/Makefile.in | 1 + .../events/nsIDOMElementReplaceEvent.idl | 24 ++ dom/tests/mochitest/Makefile.in | 1 + .../mochitest/general/test_interfaces.html | 3 +- dom/tests/mochitest/webcomponents/Makefile.in | 18 ++ .../webcomponents/test_document_register.html | 79 ++++++ .../test_document_register_lifecycle.html | 49 ++++ js/xpconnect/src/dictionary_helper_gen.conf | 5 +- js/xpconnect/src/event_impl_gen.conf.in | 1 + 16 files changed, 548 insertions(+), 3 deletions(-) create mode 100644 dom/interfaces/events/nsIDOMElementReplaceEvent.idl create mode 100644 dom/tests/mochitest/webcomponents/Makefile.in create mode 100644 dom/tests/mochitest/webcomponents/test_document_register.html create mode 100644 dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 23bd89c0e7ac..9479af3f9249 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -61,6 +61,7 @@ #include "nsTreeWalker.h" #include "nsIServiceManager.h" +#include "nsDOMClassInfo.h" #include "nsContentCID.h" #include "nsError.h" @@ -135,10 +136,12 @@ #include "nsIPrompt.h" #include "nsIPropertyBag2.h" #include "nsIDOMPageTransitionEvent.h" +#include "nsJSUtils.h" #include "nsFrameLoader.h" #include "nsEscape.h" #include "nsObjectLoadingContent.h" #include "nsHtml5TreeOpExecutor.h" +#include "nsIDOMElementReplaceEvent.h" #ifdef MOZ_MEDIA #include "nsHTMLMediaElement.h" #endif // MOZ_MEDIA @@ -168,6 +171,7 @@ #include "mozilla/dom/Link.h" #include "nsXULAppAPI.h" #include "nsDOMTouchEvent.h" +#include "DictionaryHelpers.h" #include "mozilla/Preferences.h" @@ -189,6 +193,8 @@ nsWeakPtr nsDocument::sFullScreenDoc = nullptr; // which requested DOM full-screen mode. nsWeakPtr nsDocument::sFullScreenRootDoc = nullptr; +nsIDOMElement* nsDocument::sCurrentUpgradeElement = nullptr; + #ifdef PR_LOGGING static PRLogModuleInfo* gDocumentLeakPRLog; static PRLogModuleInfo* gCspPRLog; @@ -1369,6 +1375,11 @@ nsDocument::~nsDocument() mInDestructor = true; mInUnlinkOrDeletion = true; + nsISupports* supports; + QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast(&supports)); + NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!"); + nsContentUtils::DropJSObjects(supports); + // Clear mObservers to keep it in sync with the mutationobserver list mObservers.Clear(); @@ -1694,7 +1705,25 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +struct CustomPrototypeTraceArgs { + TraceCallback callback; + void* closure; +}; + + +static PLDHashOperator +CustomPrototypeTrace(const nsAString& aName, JSObject* aObject, void *aArg) +{ + CustomPrototypeTraceArgs* traceArgs = static_cast(aArg); + MOZ_ASSERT(aObject, "Protocol object value must not be null"); + traceArgs->callback(aObject, "mCustomPrototypes entry", traceArgs->closure); + return PL_DHASH_NEXT; +} + + NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument) + CustomPrototypeTraceArgs customPrototypeArgs = { aCallback, aClosure }; + tmp->mCustomPrototypes.EnumerateRead(CustomPrototypeTrace, &customPrototypeArgs); nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END @@ -1758,6 +1787,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) tmp->mIdentifierMap.Clear(); + tmp->mCustomPrototypes.Clear(); + if (tmp->mAnimationController) { tmp->mAnimationController->Unlink(); } @@ -1782,6 +1813,7 @@ nsDocument::Init() mIdentifierMap.Init(); mStyledLinks.Init(); mRadioGroups.Init(); + mCustomPrototypes.Init(); // Force initialization. nsINode::nsSlots* slots = Slots(); @@ -1818,6 +1850,15 @@ nsDocument::Init() mImageTracker.Init(); mPlugins.Init(); + nsXPCOMCycleCollectionParticipant* participant; + CallQueryInterface(this, &participant); + NS_ASSERTION(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!"); + + nsISupports* thisSupports; + QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast(&thisSupports)); + NS_ASSERTION(thisSupports, "Failed to QI to nsCycleCollectionISupports!"); + nsContentUtils::HoldJSObjects(thisSupports, participant); + return NS_OK; } @@ -4582,6 +4623,218 @@ nsDocument::GetElementsByTagName(const nsAString& aTagname, return NS_OK; } +static JSBool +CustomElementConstructor(JSContext *aCx, unsigned aArgc, JS::Value* aVp) +{ + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); + + JSObject* global = JS_GetGlobalForObject(aCx, &args.callee()); + nsCOMPtr window = do_QueryWrapper(aCx, global); + MOZ_ASSERT(window, "Should have a non-null window"); + + nsIDocument* document = window->GetDoc(); + + // Function name is the type of the custom element. + JSString* jsFunName = JS_GetFunctionId(JS_ValueToFunction(aCx, + args.calleev())); + nsDependentJSString elemName; + if (!elemName.init(aCx, jsFunName)) { + return false; + } + + nsCOMPtr newElement; + nsresult rv = document->CreateElem(elemName, nullptr, kNameSpaceID_XHTML, + getter_AddRefs(newElement)); + JS::Value v; + rv = nsContentUtils::WrapNative(aCx, global, newElement, + (nsWrapperCache*) nullptr, &v); + NS_ENSURE_SUCCESS(rv, false); + + JS_SET_RVAL(aCx, aVp, v); + return true; +} + +static nsresult +GetPrototypeFromClassInfoId(JSContext* aCx, JSObject* aScope, + nsDOMClassInfoID aClassInfoId, + JSObject** aPrototype) +{ + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + nsIClassInfo* classInfo = NS_GetDOMClassInfoInstance(aClassInfoId); + NS_ENSURE_TRUE(classInfo, NS_ERROR_UNEXPECTED); + + nsCOMPtr holder; + nsresult rv = xpc->GetWrappedNativePrototype(aCx, aScope, classInfo, + getter_AddRefs(holder)); + NS_ENSURE_SUCCESS(rv, rv); + + JSObject* interface; + rv = holder->GetJSObject(&interface); + NS_ENSURE_SUCCESS(rv, rv); + + *aPrototype = interface; + return NS_OK; +} + +NS_IMETHODIMP +nsDocument::Register(const nsAString& aName, const JS::Value& aOptions, + JSContext* aCx, uint8_t aArgc, + jsval* aConstructor /* out param */) +{ + nsAutoString lcName; + nsContentUtils::ASCIIToLower(aName, lcName); + + NS_ENSURE_TRUE(StringBeginsWith(lcName, NS_LITERAL_STRING("x-")), + NS_ERROR_DOM_INVALID_CHARACTER_ERR); + + nsresult rv = nsContentUtils::CheckQName(lcName, false); + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INVALID_CHARACTER_ERR); + + DocumentRegisterOptions options; + nsCOMPtr lifecycleCallback; + + if (aArgc > 0) { + rv = options.Init(aCx, &aOptions); + NS_ENSURE_SUCCESS(rv, rv); + + if (options.lifecycle) { + JS::Value callbacksValue; + rv = options.lifecycle->GetAsJSVal(&callbacksValue); + NS_ENSURE_SUCCESS(rv, rv); + + LifecycleCallbacks callbacks; + rv = callbacks.Init(aCx, &callbacksValue); + NS_ENSURE_SUCCESS(rv, rv); + + lifecycleCallback = callbacks.created; + } + } + + // TODO(wchen): Templates are not currently supported. Bug 818976. + if (options.customTemplate) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsIScriptGlobalObject* sgo = GetScopeObject(); + NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); + JSObject* global = sgo->GetGlobalJSObject(); + + JSObject* protoObject; + if (!options.prototype) { + // If a prototype is not provided, we use the interface prototype for + // HTMLSpanElement. + rv = GetPrototypeFromClassInfoId(aCx, global, + eDOMClassInfo_HTMLSpanElement_id, + &protoObject); + NS_ENSURE_SUCCESS(rv, rv); + } else { + JS::Value customProto; + rv = options.prototype->GetAsJSVal(&customProto); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ENSURE_TRUE(!customProto.isPrimitive(), NS_ERROR_TYPE_ERR); + + protoObject = &customProto.toObject(); + + // If a prototype is provided, we must check to ensure that it inherits + // from HTMLElement. + JSObject* htmlProto; + rv = GetPrototypeFromClassInfoId(aCx, global, + eDOMClassInfo_HTMLElement_id, + &htmlProto); + NS_ENSURE_SUCCESS(rv, rv); + + // Check the proto chain for HTMLElement prototype. + JSObject* protoProto; + NS_ENSURE_TRUE(JS_GetPrototype(aCx, protoObject, &protoProto), + NS_ERROR_UNEXPECTED); + while (protoProto) { + if (protoProto == htmlProto) { + break; + } + NS_ENSURE_TRUE(JS_GetPrototype(aCx, protoProto, &protoProto), + NS_ERROR_UNEXPECTED); + } + + NS_ENSURE_TRUE(protoProto, NS_ERROR_DOM_TYPE_MISMATCH_ERR); + } + + // Associate the prototype with the custom element. + mCustomPrototypes.Put(lcName, protoObject); + + // Do element upgrade. + nsRefPtr list = GetElementsByTagName(lcName); + for (int32_t i = 0; i < list->Length(false); i++) { + nsINode* oldNode = list->Item(i, false); + + // TODO(wchen): Perform upgrade on Shadow DOM when implemented. + // Bug 806506. + nsCOMPtr newNode; + rv = nsNodeUtils::Clone(oldNode, true, getter_AddRefs(newNode)); + NS_ENSURE_SUCCESS(rv, rv); + + nsINode* parentNode = oldNode->GetParentNode(); + MOZ_ASSERT(parentNode, "Node obtained by GetElementsByTagName."); + nsCOMPtr newElement = do_QueryInterface(newNode); + MOZ_ASSERT(newElement, "Cloned of node obtained by GetElementsByTagName."); + + ErrorResult error; + parentNode->ReplaceChild(*newNode, *oldNode, error); + rv = error.ErrorCode(); + NS_ENSURE_SUCCESS(rv, rv); + + // Dispatch elementreplaced to replaced elements. + nsCOMPtr event; + rv = CreateEvent(NS_LITERAL_STRING("elementreplace"), getter_AddRefs(event)); + NS_ENSURE_SUCCESS(rv, rv); + + if (lifecycleCallback) { + // Update static member used for "this" translation + // when calling callback. + sCurrentUpgradeElement = newElement.get(); + lifecycleCallback->Created(nullptr); + sCurrentUpgradeElement = nullptr; + } + + nsCOMPtr ptEvent = do_QueryInterface(event); + MOZ_ASSERT(ptEvent); + + rv = ptEvent->InitElementReplaceEvent(NS_LITERAL_STRING("elementreplace"), + false, false, newElement); + NS_ENSURE_SUCCESS(rv, rv); + + event->SetTrusted(true); + event->SetTarget(oldNode); + nsEventDispatcher::DispatchDOMEvent(oldNode, nullptr, event, + nullptr, nullptr); + } + + nsContentUtils::DispatchTrustedEvent(this, static_cast(this), + NS_LITERAL_STRING("elementupgrade"), + true, true); + + // Create constructor to return. Store the name of the custom element as the + // name of the function. + JSFunction* constructor = JS_NewFunction(aCx, CustomElementConstructor, 0, + JSFUN_CONSTRUCTOR, nullptr, + NS_ConvertUTF16toUTF8(lcName).get()); + JSObject* constructorObject = JS_GetFunctionObject(constructor); + + JS::Value protoVal = OBJECT_TO_JSVAL(protoObject); + NS_ENSURE_TRUE(JS_SetProperty(aCx, constructorObject, "prototype", + &protoVal), NS_ERROR_UNEXPECTED); + + JS::Value constructorVal = OBJECT_TO_JSVAL(constructorObject); + NS_ENSURE_TRUE(JS_SetProperty(aCx, protoObject, "constructor", + &constructorVal), NS_ERROR_UNEXPECTED); + + // Return the constructor. + *aConstructor = OBJECT_TO_JSVAL(constructorObject); + + return NS_OK; +} + already_AddRefed nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, const nsAString& aLocalName) diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 4c4cb78cdb4a..f050dfea9a32 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1005,6 +1005,18 @@ public: // DocSizeOfIncludingThis is inherited from nsIDocument. virtual nsIDOMNode* AsDOMNode() { return this; } + + static nsIDOMElement* CurrentUpgradeElement() + { + return sCurrentUpgradeElement; + } + + JSObject* GetCustomPrototype(const nsAString& aElementName) + { + JSObject* prototype = nullptr; + mCustomPrototypes.Get(aElementName, &prototype); + return prototype; + } protected: friend class nsNodeUtils; @@ -1162,6 +1174,14 @@ protected: // pop one off this stack, restoring the previous full-screen state nsTArray mFullScreenStack; + // Weak reference to the current upgraded element in document.register. + // This member is used for function |this| translation when calling a + // callback interface in document.register. + static nsIDOMElement* sCurrentUpgradeElement; + + // Hashtable for custom element prototypes in web components. + nsDataHashtable mCustomPrototypes; + nsRefPtr mListenerManager; nsCOMPtr mDOMStyleSheets; nsRefPtr mStyleSheetSetList; diff --git a/content/base/src/nsNodeUtils.h b/content/base/src/nsNodeUtils.h index 15abe70bcfe4..1bc36f355e59 100644 --- a/content/base/src/nsNodeUtils.h +++ b/content/base/src/nsNodeUtils.h @@ -156,6 +156,16 @@ public: nullptr, aNodesWithProperties, nullptr, aResult); } + /** + * Clones aNode, its attributes and, if aDeep is true, its descendant nodes + */ + static nsresult Clone(nsINode *aNode, bool aDeep, nsINode **aResult) + { + nsCOMArray dummyNodeWithProperties; + return CloneAndAdopt(aNode, true, aDeep, nullptr, nullptr, nullptr, + dummyNodeWithProperties, aNode->GetParent(), aResult); + } + /** * Walks aNode, its attributes and descendant nodes. If aNewNodeInfoManager is * not null, it is used to create new nodeinfos for the nodes. Also reparents diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 7c4815e65c62..45321d5b2725 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -816,6 +816,8 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, if (aEventType.LowerCaseEqualsLiteral("commandevent") || aEventType.LowerCaseEqualsLiteral("commandevents")) return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nullptr); + if (aEventType.LowerCaseEqualsLiteral("elementreplace")) + return NS_NewDOMElementReplaceEvent(aDOMEvent, aPresContext, nullptr); if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") || aEventType.LowerCaseEqualsLiteral("datacontainerevents")) return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nullptr); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 578b46015fb1..5efc167917df 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -2360,6 +2360,9 @@ nsDOMClassInfo::Init() nsCOMPtr elt = new nsEventListenerThisTranslator(); sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener), elt); + nsCOMPtr lct = new nsLifecycleCallbacksThisTranslator(); + sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsILifecycleCallback), lct); + nsCOMPtr sm = do_GetService("@mozilla.org/scriptsecuritymanager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); @@ -7941,6 +7944,13 @@ nsElementSH::PreCreate(nsISupports *nativeObj, JSContext *cx, } #endif + nsAutoString elementName; + nsContentUtils::ASCIIToLower(element->NodeName(), elementName); + if (StringBeginsWith(elementName, NS_LITERAL_STRING("x-"))) { + // Don't allow slim wrappers for custom elements. + return rv == NS_SUCCESS_ALLOW_SLIM_WRAPPERS ? NS_OK : rv; + } + nsIDocument *doc = element->HasFlag(NODE_FORCE_XBL_BINDINGS) ? element->OwnerDoc() : element->GetCurrentDoc(); @@ -7986,6 +7996,17 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } #endif + // If we have a registered x-tag then we fix the prototype. + nsAutoString elementName; + nsContentUtils::ASCIIToLower(element->NodeName(), elementName); + if (StringBeginsWith(elementName, NS_LITERAL_STRING("x-"))) { + nsDocument* document = static_cast(element->OwnerDoc()); + JSObject* prototype = document->GetCustomPrototype(elementName); + if (prototype) { + return JS_SetPrototype(cx, obj, prototype) ? NS_OK : NS_ERROR_UNEXPECTED; + } + } + nsIDocument* doc; if (element->HasFlag(NODE_FORCE_XBL_BINDINGS)) { doc = element->OwnerDoc(); @@ -10197,6 +10218,24 @@ nsEventListenerThisTranslator::TranslateThis(nsISupports *aInitialThis, return NS_OK; } +NS_INTERFACE_MAP_BEGIN(nsLifecycleCallbacksThisTranslator) + NS_INTERFACE_MAP_ENTRY(nsIXPCFunctionThisTranslator) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + + +NS_IMPL_ADDREF(nsLifecycleCallbacksThisTranslator) +NS_IMPL_RELEASE(nsLifecycleCallbacksThisTranslator) + + +NS_IMETHODIMP +nsLifecycleCallbacksThisTranslator::TranslateThis(nsISupports *aInitialThis, + nsISupports **_retval) +{ + NS_IF_ADDREF(*_retval = nsDocument::CurrentUpgradeElement()); + return NS_OK; +} + NS_IMETHODIMP nsDOMConstructorSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj) diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 8324487dabfb..978b2fe09436 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -1208,6 +1208,24 @@ public: NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR }; +class nsLifecycleCallbacksThisTranslator : public nsIXPCFunctionThisTranslator +{ +public: + nsLifecycleCallbacksThisTranslator() + { + } + + virtual ~nsLifecycleCallbacksThisTranslator() + { + } + + // nsISupports + NS_DECL_ISUPPORTS + + // nsIXPCFunctionThisTranslator + NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR +}; + class nsDOMConstructorSH : public nsDOMGenericSH { protected: diff --git a/dom/interfaces/core/nsIDOMDocument.idl b/dom/interfaces/core/nsIDOMDocument.idl index 8bfc6ba833a1..b08d907ac21a 100644 --- a/dom/interfaces/core/nsIDOMDocument.idl +++ b/dom/interfaces/core/nsIDOMDocument.idl @@ -27,7 +27,7 @@ interface nsIDOMLocation; * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */ -[scriptable, uuid(31ce7ae7-15d5-4fc8-912b-ae0e23e93146)] +[scriptable, uuid(6f3aac2e-ae11-487a-9eb0-0e12c66b3b21)] interface nsIDOMDocument : nsIDOMNode { readonly attribute nsIDOMDocumentType doctype; @@ -47,6 +47,11 @@ interface nsIDOMDocument : nsIDOMNode raises(DOMException); nsIDOMNodeList getElementsByTagName(in DOMString tagname); + [optional_argc, + implicit_jscontext] jsval register(in DOMString name, + [optional] in jsval options) + raises(DOMException); + // Introduced in DOM Level 2: [optional_argc] nsIDOMNode importNode(in nsIDOMNode importedNode, [optional] in boolean deep) @@ -396,3 +401,24 @@ interface nsIDOMDocument : nsIDOMNode */ readonly attribute DOMString compatMode; }; + +/** + * Interface for lifecycle callbacks in document.register. + */ + +[scriptable, function, uuid(bfcd6299-081e-45d8-99f6-cb06fe2e70de)] +interface nsILifecycleCallback : nsISupports +{ + void created(in nsIDOMElement dummy); +}; + +dictionary LifecycleCallbacks { + nsILifecycleCallback created; +}; + +dictionary DocumentRegisterOptions { + nsIVariant prototype; + nsIDOMDocumentFragment customTemplate; + nsIVariant lifecycle; +}; + diff --git a/dom/interfaces/events/Makefile.in b/dom/interfaces/events/Makefile.in index 232af1e077bf..f4f9a22ca0ce 100644 --- a/dom/interfaces/events/Makefile.in +++ b/dom/interfaces/events/Makefile.in @@ -47,6 +47,7 @@ XPIDLSRCS = \ nsIDOMUserProximityEvent.idl \ nsIDOMDeviceOrientationEvent.idl \ nsIDOMDeviceMotionEvent.idl \ + nsIDOMElementReplaceEvent.idl \ nsIDOMScrollAreaEvent.idl \ nsIDOMTransitionEvent.idl \ nsIDOMAnimationEvent.idl \ diff --git a/dom/interfaces/events/nsIDOMElementReplaceEvent.idl b/dom/interfaces/events/nsIDOMElementReplaceEvent.idl new file mode 100644 index 000000000000..5be2849ea202 --- /dev/null +++ b/dom/interfaces/events/nsIDOMElementReplaceEvent.idl @@ -0,0 +1,24 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIDOMEvent.idl" + +interface nsIDOMElement; + +[scriptable, builtinclass, uuid(f57f7c46-d420-4f32-a61b-0eb585d30ee1)] +interface nsIDOMElementReplaceEvent : nsIDOMEvent +{ + readonly attribute nsIDOMElement upgrade; + + void initElementReplaceEvent(in DOMString typeArg, + in boolean canBubbleArg, + in boolean canCancelArg, + in nsIDOMElement upgrade); +}; + +dictionary ElementReplaceEventInit : EventInit +{ + nsIDOMElement upgrade; +}; diff --git a/dom/tests/mochitest/Makefile.in b/dom/tests/mochitest/Makefile.in index 5de932179d44..fbebe6a84005 100644 --- a/dom/tests/mochitest/Makefile.in +++ b/dom/tests/mochitest/Makefile.in @@ -27,6 +27,7 @@ DIRS += \ storageevent \ pointerlock \ webapps \ + webcomponents \ $(NULL) #needs IPC support, also tests do not run successfully in Firefox for now diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 6bc588a97feb..2e63d75df237 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -528,7 +528,8 @@ var interfaceNamesInGlobalScope = "RTCPeerConnection", "LocalMediaStream", "CSSConditionRule", - "CSSGroupingRule" + "CSSGroupingRule", + "ElementReplaceEvent" ] for (var i in SpecialPowers.Components.interfaces) { diff --git a/dom/tests/mochitest/webcomponents/Makefile.in b/dom/tests/mochitest/webcomponents/Makefile.in new file mode 100644 index 000000000000..aabdefe0590d --- /dev/null +++ b/dom/tests/mochitest/webcomponents/Makefile.in @@ -0,0 +1,18 @@ +# 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/. + +DEPTH = @DEPTH@ +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = @relativesrcdir@ + +include $(DEPTH)/config/autoconf.mk + +MOCHITEST_FILES = \ + test_document_register.html \ + test_document_register_lifecycle.html \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/dom/tests/mochitest/webcomponents/test_document_register.html b/dom/tests/mochitest/webcomponents/test_document_register.html new file mode 100644 index 000000000000..6c23d41ff996 --- /dev/null +++ b/dom/tests/mochitest/webcomponents/test_document_register.html @@ -0,0 +1,79 @@ + + + + + Test for document.register using custom prototype + + + + + + + +Bug 783129 + +
    +
    + + diff --git a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html new file mode 100644 index 000000000000..2b93cb992a27 --- /dev/null +++ b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html @@ -0,0 +1,49 @@ + + + + + Test for document.register lifecycle callback + + + + + + + +Bug 783129 + +
    +
    + + diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index a1fa30d23a93..8e23b70748f4 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -14,6 +14,8 @@ dictionaries = [ [ 'DOMFileMetadataParameters', 'nsIDOMLockedFile.idl' ], [ 'XMLHttpRequestParameters', 'nsIXMLHttpRequest.idl' ], [ 'DeviceStorageEnumerationParameters', 'nsIDOMDeviceStorage.idl' ], + [ 'DocumentRegisterOptions', 'nsIDOMDocument.idl' ], + [ 'LifecycleCallbacks', 'nsIDOMDocument.idl' ], [ 'CameraSize', 'nsIDOMCameraManager.idl' ], [ 'CameraRegion', 'nsIDOMCameraManager.idl' ], [ 'CameraPosition', 'nsIDOMCameraManager.idl' ], @@ -34,5 +36,6 @@ special_includes = [ # name of the type to not include using #include "typename.h" exclude_automatic_type_include = [ 'nsISupports', - 'mozIDOMApplication' + 'mozIDOMApplication', + 'nsILifecycleCallback' ] diff --git a/js/xpconnect/src/event_impl_gen.conf.in b/js/xpconnect/src/event_impl_gen.conf.in index 81d664ff825d..cfc33ba2284d 100644 --- a/js/xpconnect/src/event_impl_gen.conf.in +++ b/js/xpconnect/src/event_impl_gen.conf.in @@ -32,6 +32,7 @@ simple_events = [ 'MozWifiConnectionInfoEvent', 'MozCellBroadcastEvent', #endif + 'ElementReplaceEvent', 'DeviceStorageChangeEvent', 'PopupBlockedEvent' ] From 74d3eb2b1777e7a7b982ddd1302a3d67a845ba0b Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Fri, 21 Dec 2012 17:32:14 -0500 Subject: [PATCH 149/217] Bug 802827 - Android blacklisting: Use the Android version RELEASE string, instead of the SDK_INT number - r=joedrew --- widget/android/GfxInfo.cpp | 36 +++++++++++++++++++++++++++++------- widget/android/GfxInfo.h | 5 +++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index 35923b149b70..cb4a4cd62e15 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -8,6 +8,7 @@ #include "prenv.h" #include "prprf.h" #include "nsHashKeys.h" +#include "nsVersionComparator.h" #include "AndroidBridge.h" @@ -134,15 +135,31 @@ GfxInfo::EnsureInitializedFromGfxInfoData() mAdapterDescription.AppendPrintf(", Manufacturer: %s", NS_LossyConvertUTF16toASCII(mManufacturer).get()); } - int32_t signedVersion; - if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &signedVersion)) - signedVersion = 0; - mOSVersion = signedVersion; + int32_t sdkVersion; + if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &sdkVersion)) + sdkVersion = 0; // the HARDWARE field isn't available on Android SDK < 8 - if (mOSVersion >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", mHardware)) { + if (sdkVersion >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", mHardware)) { mAdapterDescription.AppendPrintf(", Hardware: %s", NS_LossyConvertUTF16toASCII(mHardware).get()); } + + nsString release; + mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "RELEASE", release); + mOSVersion = NS_LossyConvertUTF16toASCII(release); + + mOSVersionInteger = 0; + char a[5], b[5], c[5], d[5]; + SplitDriverVersion(mOSVersion.get(), a, b, c, d); + uint8_t na = atoi(a); + uint8_t nb = atoi(b); + uint8_t nc = atoi(c); + uint8_t nd = atoi(d); + + mOSVersionInteger = (uint32_t(na) << 24) | + (uint32_t(nb) << 16) | + (uint32_t(nc) << 8) | + uint32_t(nd); } AddCrashReportAnnotations(); @@ -333,12 +350,12 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aFeature == FEATURE_STAGEFRIGHT) { NS_LossyConvertUTF16toASCII cManufacturer(mManufacturer); NS_LossyConvertUTF16toASCII cModel(mModel); - if (mOSVersion < 14 /* Before version 4.0 */ ) + if (CompareVersions(mOSVersion.get(), "4.0.0") < 0) { *aStatus = nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION; return NS_OK; } - else if (mOSVersion < 16 /* Before version 4.1 */ ) + else if (CompareVersions(mOSVersion.get(), "4.1.0") < 0) { bool isWhitelisted = cManufacturer.Equals("samsung", nsCaseInsensitiveCStringComparator()) || @@ -412,3 +429,8 @@ nsString GfxInfo::Manufacturer() const { return mManufacturer; } + +uint32_t GfxInfo::OperatingSystemVersion() const +{ + return mOSVersionInteger; +} diff --git a/widget/android/GfxInfo.h b/widget/android/GfxInfo.h index 734a68d6bb86..3b9b114e3f13 100644 --- a/widget/android/GfxInfo.h +++ b/widget/android/GfxInfo.h @@ -58,7 +58,7 @@ public: NS_DECL_NSIGFXINFODEBUG #endif - virtual uint32_t OperatingSystemVersion() const { return mOSVersion; } + virtual uint32_t OperatingSystemVersion() const; protected: @@ -85,9 +85,10 @@ private: nsCString mAdapterDescription; OperatingSystem mOS; - uint32_t mOSVersion; nsString mModel, mHardware, mManufacturer, mProduct; + nsCString mOSVersion; + uint32_t mOSVersionInteger; }; } // namespace widget From eba9694bad068132e13999f071c6acf44f791792 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Fri, 21 Dec 2012 17:32:15 -0500 Subject: [PATCH 150/217] Bug 802827 - report Stagefright feature in AppNotes - r=joedrew --- content/media/plugins/MediaPluginHost.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/media/plugins/MediaPluginHost.cpp b/content/media/plugins/MediaPluginHost.cpp index 872db4625939..797b57e97fed 100644 --- a/content/media/plugins/MediaPluginHost.cpp +++ b/content/media/plugins/MediaPluginHost.cpp @@ -14,6 +14,7 @@ #include "pratom.h" #include "MediaPluginReader.h" #include "nsIGfxInfo.h" +#include "gfxCrashReporterUtils.h" #include "MPAPI.h" @@ -113,6 +114,8 @@ static bool IsOmxSupported() return false; } + ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled); + if (!forceEnabled) { nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); if (gfxInfo) { @@ -125,6 +128,8 @@ static bool IsOmxSupported() } } } + + reporter.SetSuccessful(); return true; } From 3169031dfa4dcbcc1cf704d75b7e13d9321e9324 Mon Sep 17 00:00:00 2001 From: Scott Johnson Date: Fri, 21 Dec 2012 16:36:31 -0600 Subject: [PATCH 151/217] Bug 654352: Implement (blassey) and fix (jwir3) document.caretPositionFromPoint so that it utilizes frame-relative coordinates to get caret position. (original: [r=smaug,roc]) (fixes: [r=blassey,Ms2ger]) --- content/base/src/Makefile.in | 1 + content/base/src/nsDOMCaretPosition.cpp | 40 ++++++++++++++++ content/base/src/nsDOMCaretPosition.h | 63 ++++++++++++++++++++++++ content/base/src/nsDocument.cpp | 62 ++++++++++++++++++++++++ content/base/test/Makefile.in | 2 + content/base/test/test_bug654352-2.html | 64 +++++++++++++++++++++++++ content/base/test/test_bug654352.html | 50 +++++++++++++++++++ dom/bindings/Bindings.conf | 4 ++ dom/interfaces/core/nsIDOMDocument.idl | 15 +++++- dom/webidl/CaretPosition.webidl | 12 +++++ dom/webidl/WebIDL.mk | 1 + 11 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 content/base/src/nsDOMCaretPosition.cpp create mode 100644 content/base/src/nsDOMCaretPosition.h create mode 100644 content/base/test/test_bug654352-2.html create mode 100644 content/base/test/test_bug654352.html create mode 100644 dom/webidl/CaretPosition.webidl diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index c9684b58c9fb..29a4cb1dbe33 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -74,6 +74,7 @@ CPPSRCS = \ nsDOMAttribute.cpp \ nsDOMAttributeMap.cpp \ nsDOMBlobBuilder.cpp \ + nsDOMCaretPosition.cpp \ nsDOMDocumentType.cpp \ nsDOMFile.cpp \ nsDOMFileReader.cpp \ diff --git a/content/base/src/nsDOMCaretPosition.cpp b/content/base/src/nsDOMCaretPosition.cpp new file mode 100644 index 000000000000..5a287d94103a --- /dev/null +++ b/content/base/src/nsDOMCaretPosition.cpp @@ -0,0 +1,40 @@ +/* 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/. */ + +#include "nsDOMCaretPosition.h" +#include "mozilla/dom/CaretPositionBinding.h" +#include "nsContentUtils.h" + +nsDOMCaretPosition::nsDOMCaretPosition(nsINode* aNode, uint32_t aOffset) + : mOffset(aOffset), mOffsetNode(aNode) +{ + SetIsDOMBinding(); +} + +nsDOMCaretPosition::~nsDOMCaretPosition() +{ +} + +nsINode* nsDOMCaretPosition::GetOffsetNode() const +{ + return mOffsetNode; +} + +JSObject* +nsDOMCaretPosition::WrapObject(JSContext *aCx, JSObject *aScope, + bool *aTried) +{ + return mozilla::dom::CaretPositionBinding::Wrap(aCx, aScope, this, aTried); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCaretPosition, mOffsetNode) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCaretPosition) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCaretPosition) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCaretPosition) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + diff --git a/content/base/src/nsDOMCaretPosition.h b/content/base/src/nsDOMCaretPosition.h new file mode 100644 index 000000000000..88b6df67e75f --- /dev/null +++ b/content/base/src/nsDOMCaretPosition.h @@ -0,0 +1,63 @@ +/* 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/. */ + +#ifndef nsDOMCaretPosition_h +#define nsDOMCaretPosition_h + +#include "nsCycleCollectionParticipant.h" +#include "nsCOMPtr.h" +#include "nsINode.h" +#include "nsWrapperCache.h" + +/** + * Implementation of a DOM Caret Position, which is a node and offset within + * that node, in the DOM tree. + * + * http://www.w3.org/TR/cssom-view/#dom-documentview-caretrangefrompoint + * + * @see Document::mozCaretPositionFromPoint(float x, float y) + */ +class nsDOMCaretPosition : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCaretPosition) + + nsDOMCaretPosition(nsINode* aNode, uint32_t aOffset); + + /** + * Retrieve the offset (character position within the DOM node) of the + * CaretPosition. + * + * @returns The offset within the DOM node. + */ + uint32_t Offset() const { return mOffset; } + + /** + * Retrieve the DOM node with which this CaretPosition was established. + * Normally, this will be created from a point, so it will be the DOM + * node that lies at the point specified. + * + * @returns The DOM node of the CaretPosition. + * + * @see Document::mozCaretPositionFromPoint(float x, float y) + */ + nsINode* GetOffsetNode() const; + + nsISupports* GetParentObject() const + { + return GetOffsetNode(); + } + + virtual JSObject* WrapObject(JSContext *aCx, JSObject *aScope, bool *aTried) + MOZ_OVERRIDE MOZ_FINAL; + +protected: + virtual ~nsDOMCaretPosition(); + uint32_t mOffset; + nsCOMPtr mOffsetNode; +}; +#endif + diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 9479af3f9249..d2cd9086d090 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -181,6 +181,10 @@ #include "nsSandboxFlags.h" #include "nsIAppsService.h" +#include "nsFrame.h" +#include "nsDOMCaretPosition.h" +#include "nsIDOMHTMLTextAreaElement.h" + using namespace mozilla; using namespace mozilla::dom; @@ -8650,6 +8654,64 @@ ResetFullScreen(nsIDocument* aDocument, void* aData) return true; } +NS_IMETHODIMP +nsDocument::MozCaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos) +{ + NS_ENSURE_ARG_POINTER(aCaretPos); + *aCaretPos = nullptr; + + nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); + nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); + nsPoint pt(x, y); + + nsIPresShell *ps = GetShell(); + if (!ps) { + return NS_OK; + } + + nsIFrame *rootFrame = ps->GetRootFrame(); + + // XUL docs, unlike HTML, have no frame tree until everything's done loading + if (!rootFrame) { + return NS_OK; // return null to premature XUL callers as a reminder to wait + } + + nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, true, + false); + if (!ptFrame) { + return NS_OK; + } + + // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need + // to adjust to frame-relative coordinates before we can perform this call. + // It should also not take into account the padding of the frame. + nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame); + + nsFrame::ContentOffsets offsets = + ptFrame->GetContentOffsetsFromPoint(adjustedPoint); + + nsCOMPtr node = offsets.content; + uint32_t offset = offsets.offset; + if (node && node->IsInNativeAnonymousSubtree()) { + nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent(); + nsCOMPtr input = do_QueryInterface(nonanon); + nsCOMPtr textArea = do_QueryInterface(nonanon); + bool isText; + if (textArea || (input && + NS_SUCCEEDED(input->MozIsTextField(false, &isText)) && + isText)) { + node = nonanon; + } else { + node = nullptr; + offset = 0; + } + } + + *aCaretPos = new nsDOMCaretPosition(node, offset); + NS_ADDREF(*aCaretPos); + return NS_OK; +} + /* static */ void nsDocument::ExitFullScreen() diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index e756d86fc2ba..5d10b02acaf6 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -503,6 +503,8 @@ MOCHITEST_FILES_B = \ file_html_in_xhr3.html \ file_html_in_xhr.sjs \ test_bug647518.html \ + test_bug654352.html \ + test_bug654352-2.html \ test_bug664916.html \ test_bug666604.html \ test_bug675121.html \ diff --git a/content/base/test/test_bug654352-2.html b/content/base/test/test_bug654352-2.html new file mode 100644 index 000000000000..ff57b21d804c --- /dev/null +++ b/content/base/test/test_bug654352-2.html @@ -0,0 +1,64 @@ + + + + + + + + + Test for Bug 654352 + + + + +
    abc, abc, abc
    +abc, abc, abc
    +

    +marquee +
    + + diff --git a/content/base/test/test_bug654352.html b/content/base/test/test_bug654352.html new file mode 100644 index 000000000000..f0b383c5af5c --- /dev/null +++ b/content/base/test/test_bug654352.html @@ -0,0 +1,50 @@ + + + + + Test for Bug 654352 + + + + + +Mozilla Bug 654352 +

    + +
    +
    +
    + +
    test text
    +
    +
    +
    + + + + + diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index bbc5c6022f86..7df5626a88b2 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -180,6 +180,10 @@ DOMInterfaces = { 'skipGen': True }], +'CaretPosition' : { + 'nativeType': 'nsDOMCaretPosition', +}, + 'DOMParser': { 'nativeType': 'nsDOMParser', }, diff --git a/dom/interfaces/core/nsIDOMDocument.idl b/dom/interfaces/core/nsIDOMDocument.idl index b08d907ac21a..040a4448e8b0 100644 --- a/dom/interfaces/core/nsIDOMDocument.idl +++ b/dom/interfaces/core/nsIDOMDocument.idl @@ -27,7 +27,7 @@ interface nsIDOMLocation; * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */ -[scriptable, uuid(6f3aac2e-ae11-487a-9eb0-0e12c66b3b21)] +[scriptable, uuid(d19897dc-948a-42e7-8ac6-d8a0bd141b85)] interface nsIDOMDocument : nsIDOMNode { readonly attribute nsIDOMDocumentType doctype; @@ -330,7 +330,7 @@ interface nsIDOMDocument : nsIDOMNode */ void mozSetImageElement(in DOMString aImageElementId, in nsIDOMElement aImageElement); - + /** * Element which is currently the full-screen element as per the DOM * full-screen api. @@ -372,6 +372,17 @@ interface nsIDOMDocument : nsIDOMNode */ readonly attribute nsIDOMElement mozPointerLockElement; + /** + * Retrieve the location of the caret position (DOM node and character + * offset within that node), given a point. + * + * @param x Horizontal point at which to determine the caret position, in + * page coordinates. + * @param y Vertical point at which to determine the caret position, in + * page coordinates. + */ + nsISupports /* CaretPosition */ mozCaretPositionFromPoint(in float x, in float y); + /** * Exit pointer is lock if locked, as per the DOM pointer lock api. * diff --git a/dom/webidl/CaretPosition.webidl b/dom/webidl/CaretPosition.webidl new file mode 100644 index 000000000000..4cc42759f25e --- /dev/null +++ b/dom/webidl/CaretPosition.webidl @@ -0,0 +1,12 @@ +/* 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/. */ + +interface CaretPosition { + + /** + * The offsetNode could potentially be null due to anonymous content. + */ + readonly attribute Node? offsetNode; + readonly attribute unsigned long offset; +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 5ed6f4027983..8bee83d33bdb 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -26,6 +26,7 @@ webidl_files = \ CSSValue.webidl \ CSSValueList.webidl \ DelayNode.webidl \ + CaretPosition.webidl \ DOMImplementation.webidl \ DOMParser.webidl \ DOMSettableTokenList.webidl \ From 2f9a17441eac22d3e1dc29a98890212d52c0ea7f Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Thu, 20 Dec 2012 20:46:23 -0800 Subject: [PATCH 152/217] Bug 823325 - Call blur() on onfocused browser. r=mfinkle --- mobile/android/chrome/content/browser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index f8b3dba937c8..a86344944ba4 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2812,6 +2812,7 @@ Tab.prototype = { this.browser.docShellIsActive = true; } else { this.browser.setAttribute("type", "content-targetable"); + this.browser.blur(); this.browser.docShellIsActive = false; } }, From fd988f989ebcb88a3099b9583313c39c6b997ff3 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 21 Dec 2012 14:49:21 -0800 Subject: [PATCH 153/217] Bug 822563: Pretty-print references to JSObject and its subclasses. r=sfink --- js/src/gdb/mozilla/JSObject.py | 14 ++++-- js/src/gdb/mozilla/prettyprinters.py | 59 ++++++++++++++++++------- js/src/gdb/tests/test-JSObject.cpp | 6 ++- js/src/gdb/tests/test-JSObject.py | 5 +++ js/src/gdb/tests/test-prettyprinters.py | 4 ++ 5 files changed, 66 insertions(+), 22 deletions(-) diff --git a/js/src/gdb/mozilla/JSObject.py b/js/src/gdb/mozilla/JSObject.py index f0f86bdffb96..9504caea3071 100644 --- a/js/src/gdb/mozilla/JSObject.py +++ b/js/src/gdb/mozilla/JSObject.py @@ -3,7 +3,7 @@ import gdb import mozilla.JSString import mozilla.prettyprinters as prettyprinters -from mozilla.prettyprinters import ptr_pretty_printer +from mozilla.prettyprinters import ptr_pretty_printer, ref_pretty_printer from mozilla.Root import deref prettyprinters.clear_module_printers(__name__) @@ -19,9 +19,9 @@ class JSObjectTypeCache(object): # JSFunction has JSObject as a base class. @ptr_pretty_printer('JSObject') -class JSObjectPtr(prettyprinters.Pointer): +class JSObjectPtrOrRef(prettyprinters.Pointer): def __init__(self, value, cache): - super(JSObjectPtr, self).__init__(value, cache) + super(JSObjectPtrOrRef, self).__init__(value, cache) if not cache.mod_JSObject: cache.mod_JSObject = JSObjectTypeCache(value, cache) self.otc = cache.mod_JSObject @@ -34,9 +34,15 @@ class JSObjectPtr(prettyprinters.Pointer): is_delegate = bool(flags & self.otc.flag_DELEGATE) name = None if class_name == 'Function': - function = self.value.cast(self.otc.func_ptr_type) + if self.value.type.code == gdb.TYPE_CODE_PTR: + function = self.value.cast(self.otc.func_ptr_type) + elif self.value.type.code == gdb.TYPE_CODE_REF: + function = self.value.address.cast(self.otc.func_ptr_type) atom = deref(function['atom_']) name = str(atom) if atom else '' return '[object %s%s]%s' % (class_name, ' ' + name if name else '', ' delegate' if is_delegate else '') + +@ref_pretty_printer('JSObject') +def JSObjectRef(value, cache): return JSObjectPtrOrRef(value, cache) diff --git a/js/src/gdb/mozilla/prettyprinters.py b/js/src/gdb/mozilla/prettyprinters.py index 0a0524deafef..796318326202 100644 --- a/js/src/gdb/mozilla/prettyprinters.py +++ b/js/src/gdb/mozilla/prettyprinters.py @@ -45,6 +45,20 @@ def ptr_pretty_printer(type_name): return fn return add +# a dictionary mapping gdb.Type tags to pretty-printer functions for +# references to that type. +ref_printers_by_tag = {} + +# A decorator: add the decoratee as a pretty-printer lookup function for +# references to instances of types named |type_name|. +def ref_pretty_printer(type_name): + def add(fn): + check_for_reused_pretty_printer(fn) + add_to_subprinter_list(fn, "ref-to-" + type_name) + ref_printers_by_tag[type_name] = fn + return fn + return add + # a dictionary mapping the template name portion of gdb.Type tags to # pretty-printer functions for instantiations of that template. template_printers_by_tag = {} @@ -83,7 +97,8 @@ def pretty_printer_for_regexp(pattern, name): # # clear_module_printers(__name__) def clear_module_printers(module_name): - global printers_by_tag, ptr_printers_by_tag, template_printers_by_tag, printers_by_regexp + global printers_by_tag, ptr_printers_by_tag, ref_printers_by_tag + global template_printers_by_tag, printers_by_regexp # Remove all pretty-printers defined in the module named |module_name| # from d. @@ -101,6 +116,7 @@ def clear_module_printers(module_name): clear_dictionary(printers_by_tag) clear_dictionary(ptr_printers_by_tag) + clear_dictionary(ref_printers_by_tag) clear_dictionary(template_printers_by_tag) # Iterate over printers_by_regexp, deleting entries from the given module. @@ -226,28 +242,31 @@ def lookup_for_objfile(objfile): return f(value, cache) return None + def check_table_by_type_name(table, t): + if t.code == gdb.TYPE_CODE_TYPEDEF: + return check_table(table, str(t)) + elif t.code == gdb.TYPE_CODE_STRUCT and t.tag: + return check_table(table, t.tag) + else: + return None + for t in implemented_types(value.type): if t.code == gdb.TYPE_CODE_PTR: for t2 in implemented_types(t.target()): - if t2.code == gdb.TYPE_CODE_TYPEDEF: - p = check_table(ptr_printers_by_tag, str(t2)) - elif t2.code == gdb.TYPE_CODE_STRUCT and t2.tag: - p = check_table(ptr_printers_by_tag, t2.tag) - else: - p = None + p = check_table_by_type_name(ptr_printers_by_tag, t2) + if p: return p + elif t.code == gdb.TYPE_CODE_REF: + for t2 in implemented_types(t.target()): + p = check_table_by_type_name(ref_printers_by_tag, t2) if p: return p else: - if t.code == gdb.TYPE_CODE_TYPEDEF: - p = check_table(printers_by_tag, str(t)) - elif t.code == gdb.TYPE_CODE_STRUCT and t.tag: + p = check_table_by_type_name(printers_by_tag, t) + if p: return p + if t.code == gdb.TYPE_CODE_STRUCT and t.tag: m = template_regexp.match(t.tag) if m: p = check_table(template_printers_by_tag, m.group(1)) - else: - p = check_table(printers_by_tag, t.tag) - else: - p = None - if p: return p + if p: return p # Failing that, look for a printer in printers_by_regexp. We have # to scan the whole list, so regexp printers should be used @@ -275,6 +294,8 @@ def lookup_for_objfile(objfile): # pointers, by declining to construct a pretty-printer for them at all. # Derived classes may simply assume that self.value is non-null. # +# To help share code, this class can also be used with reference types. +# # This class provides the following methods, which subclasses are free to # override: # @@ -291,7 +312,8 @@ def lookup_for_objfile(objfile): class Pointer(object): def __new__(cls, value, cache): # Don't try to provide pretty-printers for NULL pointers. - if value == 0: return None + if value.type.code == gdb.TYPE_CODE_PTR and value == 0: + return None return super(Pointer, cls).__new__(cls) def __init__(self, value, cache): @@ -301,7 +323,10 @@ class Pointer(object): def to_string(self): # See comment above. assert not hasattr(self, 'display_hint') or self.display_hint() != 'string' - address = self.value.cast(self.cache.void_ptr_t) + if self.value.type.code == gdb.TYPE_CODE_PTR: + address = self.value.cast(self.cache.void_ptr_t) + elif self.value.type.code == gdb.TYPE_CODE_REF: + address = '@' + str(self.value.address.cast(self.cache.void_ptr_t)) try: summary = self.summary() except gdb.MemoryError as r: diff --git a/js/src/gdb/tests/test-JSObject.cpp b/js/src/gdb/tests/test-JSObject.cpp index 1d7ce07718c3..ae944b046c47 100644 --- a/js/src/gdb/tests/test-JSObject.cpp +++ b/js/src/gdb/tests/test-JSObject.cpp @@ -10,6 +10,9 @@ FRAGMENT(JSObject, simple) { js::Rooted funcPtr(cx, JS_NewFunction(cx, (JSNative) 1, 0, 0, JS_GetGlobalObject(cx), "formFollows")); + JSObject &plainRef = *plain; + JSFunction &funcRef = *funcPtr; + breakpoint(); (void) glob; @@ -17,6 +20,8 @@ FRAGMENT(JSObject, simple) { (void) func; (void) anon; (void) funcPtr; + (void) &plainRef; + (void) &funcRef; } FRAGMENT(JSObject, null) { @@ -26,4 +31,3 @@ FRAGMENT(JSObject, null) { (void) null; } - diff --git a/js/src/gdb/tests/test-JSObject.py b/js/src/gdb/tests/test-JSObject.py index ddd357b91724..35122e14a5d4 100644 --- a/js/src/gdb/tests/test-JSObject.py +++ b/js/src/gdb/tests/test-JSObject.py @@ -1,6 +1,8 @@ # Printing JSObjects. assert_subprinter_registered('SpiderMonkey', 'ptr-to-JSObject') +assert_subprinter_registered('SpiderMonkey', 'ref-to-JSObject') + run_fragment('JSObject.simple') # These patterns look a little strange because of prolog.py's 'set print @@ -13,3 +15,6 @@ assert_pretty('plain', '(JSObject *) [object Object]') assert_pretty('func', '(JSObject *) [object Function "dys"]') assert_pretty('anon', '(JSObject *) [object Function ]') assert_pretty('funcPtr', '(JSFunction *) [object Function "formFollows"]') + +assert_pretty('plainRef', '(JSObject &) @ [object Object]') +assert_pretty('funcRef', '(JSFunction &) @ [object Function "formFollows"]') diff --git a/js/src/gdb/tests/test-prettyprinters.py b/js/src/gdb/tests/test-prettyprinters.py index f36eb450a391..9c380fdda8f8 100644 --- a/js/src/gdb/tests/test-prettyprinters.py +++ b/js/src/gdb/tests/test-prettyprinters.py @@ -16,3 +16,7 @@ assert_eq(implemented_type_names('e'), ['E', 'C', 'D']) assert_eq(implemented_type_names('e_'), ['E_', 'E', 'C', 'D']) assert_eq(implemented_type_names('f'), ['F', 'C', 'D']) assert_eq(implemented_type_names('h'), ['H', 'F', 'G', 'C', 'D']) + +# Check that our pretty-printers aren't interfering with printing other types. +assert_pretty('10', '10') +assert_pretty('(void*) 0', '') # Because of 'set print address off' From fa2273f6d90b425da11f0391789c48af4b667c68 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Thu, 20 Dec 2012 21:13:37 -0800 Subject: [PATCH 154/217] Bug 823390 - Kill clearParent and clearType calls in CloneScriptRegExpObject. (r=bhackett) --- js/src/vm/RegExpObject.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index f8cbc1fd5858..e2d9a786a7c7 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -795,14 +795,10 @@ js::XDRScriptRegExpObject(XDRState *xdr, HeapPtrObject *objp) return false; if (mode == XDR_DECODE) { RegExpFlag flags = RegExpFlag(flagsword); - Rooted reobj(xdr->cx(), RegExpObject::createNoStatics(xdr->cx(), source, flags, NULL)); + RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx(), source, flags, NULL); if (!reobj) return false; - if (!JSObject::clearParent(xdr->cx(), reobj)) - return false; - if (!JSObject::clearType(xdr->cx(), reobj)) - return false; objp->init(reobj); } return true; @@ -820,12 +816,5 @@ js::CloneScriptRegExpObject(JSContext *cx, RegExpObject &reobj) /* NB: Keep this in sync with XDRScriptRegExpObject. */ RootedAtom source(cx, reobj.getSource()); - Rooted clone(cx, RegExpObject::createNoStatics(cx, source, reobj.getFlags(), NULL)); - if (!clone) - return NULL; - if (!JSObject::clearParent(cx, clone)) - return NULL; - if (!JSObject::clearType(cx, clone)) - return NULL; - return clone; + return RegExpObject::createNoStatics(cx, source, reobj.getFlags(), NULL); } From 54e7b9d8749d3abe06dae62607310c499a454ca1 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Fri, 21 Dec 2012 15:04:31 -0800 Subject: [PATCH 155/217] Bug 823258 - Asking for a PROMPT_ACTION for a privileged API in a privileged packaged app - remember my choice default is incorrect. r=fabrice --- b2g/components/ContentPermissionPrompt.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/b2g/components/ContentPermissionPrompt.js b/b2g/components/ContentPermissionPrompt.js index 622727b27e60..41e29ff3e42f 100644 --- a/b2g/components/ContentPermissionPrompt.js +++ b/b2g/components/ContentPermissionPrompt.js @@ -120,6 +120,9 @@ ContentPermissionPrompt.prototype = { let principal = request.principal; let isApp = principal.appStatus != Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED; + let remember = principal.appStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED + ? true + : request.remember; let details = { type: "permission-prompt", @@ -127,7 +130,7 @@ ContentPermissionPrompt.prototype = { id: requestId, origin: principal.origin, isApp: isApp, - remember: request.remember + remember: remember }; this._permission = access; From e85f37d43c83471a92d992cf4f024898ed95da51 Mon Sep 17 00:00:00 2001 From: EKR Date: Fri, 21 Dec 2012 15:14:06 -0800 Subject: [PATCH 156/217] Bug 824097 - Fix uninitialized memory read in nr_stun_server_ctx. r=jesup --- media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c index 5f658deacaf0..b6dcdcdc6fff 100644 --- a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c +++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c @@ -193,7 +193,7 @@ int nr_stun_server_process_request(nr_stun_server_ctx *ctx, nr_socket *sock, cha char string[256]; nr_stun_message *req = 0; nr_stun_message *res = 0; - nr_stun_server_client *clnt; + nr_stun_server_client *clnt = 0; nr_stun_server_request info; int error; From daab774d8ae8d2d9bf632814ca65d333038d478b Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Fri, 21 Dec 2012 18:50:00 -0500 Subject: [PATCH 157/217] Back out bug 783129 / b0788a95a2bd (merged with e01ca7212c8a) due to test failures --- content/base/src/nsDocument.cpp | 253 ------------------ content/base/src/nsDocument.h | 20 -- content/base/src/nsNodeUtils.h | 10 - content/events/src/nsEventDispatcher.cpp | 2 - dom/base/nsDOMClassInfo.cpp | 39 --- dom/base/nsDOMClassInfo.h | 18 -- dom/interfaces/core/nsIDOMDocument.idl | 26 -- dom/interfaces/events/Makefile.in | 1 - .../events/nsIDOMElementReplaceEvent.idl | 24 -- dom/tests/mochitest/Makefile.in | 1 - .../mochitest/general/test_interfaces.html | 3 +- dom/tests/mochitest/webcomponents/Makefile.in | 18 -- .../webcomponents/test_document_register.html | 79 ------ .../test_document_register_lifecycle.html | 49 ---- js/xpconnect/src/dictionary_helper_gen.conf | 5 +- js/xpconnect/src/event_impl_gen.conf.in | 1 - 16 files changed, 2 insertions(+), 547 deletions(-) delete mode 100644 dom/interfaces/events/nsIDOMElementReplaceEvent.idl delete mode 100644 dom/tests/mochitest/webcomponents/Makefile.in delete mode 100644 dom/tests/mochitest/webcomponents/test_document_register.html delete mode 100644 dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index d2cd9086d090..8bc2d0fd7f77 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -61,7 +61,6 @@ #include "nsTreeWalker.h" #include "nsIServiceManager.h" -#include "nsDOMClassInfo.h" #include "nsContentCID.h" #include "nsError.h" @@ -136,12 +135,10 @@ #include "nsIPrompt.h" #include "nsIPropertyBag2.h" #include "nsIDOMPageTransitionEvent.h" -#include "nsJSUtils.h" #include "nsFrameLoader.h" #include "nsEscape.h" #include "nsObjectLoadingContent.h" #include "nsHtml5TreeOpExecutor.h" -#include "nsIDOMElementReplaceEvent.h" #ifdef MOZ_MEDIA #include "nsHTMLMediaElement.h" #endif // MOZ_MEDIA @@ -171,7 +168,6 @@ #include "mozilla/dom/Link.h" #include "nsXULAppAPI.h" #include "nsDOMTouchEvent.h" -#include "DictionaryHelpers.h" #include "mozilla/Preferences.h" @@ -197,8 +193,6 @@ nsWeakPtr nsDocument::sFullScreenDoc = nullptr; // which requested DOM full-screen mode. nsWeakPtr nsDocument::sFullScreenRootDoc = nullptr; -nsIDOMElement* nsDocument::sCurrentUpgradeElement = nullptr; - #ifdef PR_LOGGING static PRLogModuleInfo* gDocumentLeakPRLog; static PRLogModuleInfo* gCspPRLog; @@ -1379,11 +1373,6 @@ nsDocument::~nsDocument() mInDestructor = true; mInUnlinkOrDeletion = true; - nsISupports* supports; - QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast(&supports)); - NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!"); - nsContentUtils::DropJSObjects(supports); - // Clear mObservers to keep it in sync with the mutationobserver list mObservers.Clear(); @@ -1709,25 +1698,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -struct CustomPrototypeTraceArgs { - TraceCallback callback; - void* closure; -}; - - -static PLDHashOperator -CustomPrototypeTrace(const nsAString& aName, JSObject* aObject, void *aArg) -{ - CustomPrototypeTraceArgs* traceArgs = static_cast(aArg); - MOZ_ASSERT(aObject, "Protocol object value must not be null"); - traceArgs->callback(aObject, "mCustomPrototypes entry", traceArgs->closure); - return PL_DHASH_NEXT; -} - - NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument) - CustomPrototypeTraceArgs customPrototypeArgs = { aCallback, aClosure }; - tmp->mCustomPrototypes.EnumerateRead(CustomPrototypeTrace, &customPrototypeArgs); nsINode::Trace(tmp, aCallback, aClosure); NS_IMPL_CYCLE_COLLECTION_TRACE_END @@ -1791,8 +1762,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) tmp->mIdentifierMap.Clear(); - tmp->mCustomPrototypes.Clear(); - if (tmp->mAnimationController) { tmp->mAnimationController->Unlink(); } @@ -1817,7 +1786,6 @@ nsDocument::Init() mIdentifierMap.Init(); mStyledLinks.Init(); mRadioGroups.Init(); - mCustomPrototypes.Init(); // Force initialization. nsINode::nsSlots* slots = Slots(); @@ -1854,15 +1822,6 @@ nsDocument::Init() mImageTracker.Init(); mPlugins.Init(); - nsXPCOMCycleCollectionParticipant* participant; - CallQueryInterface(this, &participant); - NS_ASSERTION(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!"); - - nsISupports* thisSupports; - QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast(&thisSupports)); - NS_ASSERTION(thisSupports, "Failed to QI to nsCycleCollectionISupports!"); - nsContentUtils::HoldJSObjects(thisSupports, participant); - return NS_OK; } @@ -4627,218 +4586,6 @@ nsDocument::GetElementsByTagName(const nsAString& aTagname, return NS_OK; } -static JSBool -CustomElementConstructor(JSContext *aCx, unsigned aArgc, JS::Value* aVp) -{ - JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); - - JSObject* global = JS_GetGlobalForObject(aCx, &args.callee()); - nsCOMPtr window = do_QueryWrapper(aCx, global); - MOZ_ASSERT(window, "Should have a non-null window"); - - nsIDocument* document = window->GetDoc(); - - // Function name is the type of the custom element. - JSString* jsFunName = JS_GetFunctionId(JS_ValueToFunction(aCx, - args.calleev())); - nsDependentJSString elemName; - if (!elemName.init(aCx, jsFunName)) { - return false; - } - - nsCOMPtr newElement; - nsresult rv = document->CreateElem(elemName, nullptr, kNameSpaceID_XHTML, - getter_AddRefs(newElement)); - JS::Value v; - rv = nsContentUtils::WrapNative(aCx, global, newElement, - (nsWrapperCache*) nullptr, &v); - NS_ENSURE_SUCCESS(rv, false); - - JS_SET_RVAL(aCx, aVp, v); - return true; -} - -static nsresult -GetPrototypeFromClassInfoId(JSContext* aCx, JSObject* aScope, - nsDOMClassInfoID aClassInfoId, - JSObject** aPrototype) -{ - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - - nsIClassInfo* classInfo = NS_GetDOMClassInfoInstance(aClassInfoId); - NS_ENSURE_TRUE(classInfo, NS_ERROR_UNEXPECTED); - - nsCOMPtr holder; - nsresult rv = xpc->GetWrappedNativePrototype(aCx, aScope, classInfo, - getter_AddRefs(holder)); - NS_ENSURE_SUCCESS(rv, rv); - - JSObject* interface; - rv = holder->GetJSObject(&interface); - NS_ENSURE_SUCCESS(rv, rv); - - *aPrototype = interface; - return NS_OK; -} - -NS_IMETHODIMP -nsDocument::Register(const nsAString& aName, const JS::Value& aOptions, - JSContext* aCx, uint8_t aArgc, - jsval* aConstructor /* out param */) -{ - nsAutoString lcName; - nsContentUtils::ASCIIToLower(aName, lcName); - - NS_ENSURE_TRUE(StringBeginsWith(lcName, NS_LITERAL_STRING("x-")), - NS_ERROR_DOM_INVALID_CHARACTER_ERR); - - nsresult rv = nsContentUtils::CheckQName(lcName, false); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INVALID_CHARACTER_ERR); - - DocumentRegisterOptions options; - nsCOMPtr lifecycleCallback; - - if (aArgc > 0) { - rv = options.Init(aCx, &aOptions); - NS_ENSURE_SUCCESS(rv, rv); - - if (options.lifecycle) { - JS::Value callbacksValue; - rv = options.lifecycle->GetAsJSVal(&callbacksValue); - NS_ENSURE_SUCCESS(rv, rv); - - LifecycleCallbacks callbacks; - rv = callbacks.Init(aCx, &callbacksValue); - NS_ENSURE_SUCCESS(rv, rv); - - lifecycleCallback = callbacks.created; - } - } - - // TODO(wchen): Templates are not currently supported. Bug 818976. - if (options.customTemplate) { - return NS_ERROR_NOT_IMPLEMENTED; - } - - nsIScriptGlobalObject* sgo = GetScopeObject(); - NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED); - JSObject* global = sgo->GetGlobalJSObject(); - - JSObject* protoObject; - if (!options.prototype) { - // If a prototype is not provided, we use the interface prototype for - // HTMLSpanElement. - rv = GetPrototypeFromClassInfoId(aCx, global, - eDOMClassInfo_HTMLSpanElement_id, - &protoObject); - NS_ENSURE_SUCCESS(rv, rv); - } else { - JS::Value customProto; - rv = options.prototype->GetAsJSVal(&customProto); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ENSURE_TRUE(!customProto.isPrimitive(), NS_ERROR_TYPE_ERR); - - protoObject = &customProto.toObject(); - - // If a prototype is provided, we must check to ensure that it inherits - // from HTMLElement. - JSObject* htmlProto; - rv = GetPrototypeFromClassInfoId(aCx, global, - eDOMClassInfo_HTMLElement_id, - &htmlProto); - NS_ENSURE_SUCCESS(rv, rv); - - // Check the proto chain for HTMLElement prototype. - JSObject* protoProto; - NS_ENSURE_TRUE(JS_GetPrototype(aCx, protoObject, &protoProto), - NS_ERROR_UNEXPECTED); - while (protoProto) { - if (protoProto == htmlProto) { - break; - } - NS_ENSURE_TRUE(JS_GetPrototype(aCx, protoProto, &protoProto), - NS_ERROR_UNEXPECTED); - } - - NS_ENSURE_TRUE(protoProto, NS_ERROR_DOM_TYPE_MISMATCH_ERR); - } - - // Associate the prototype with the custom element. - mCustomPrototypes.Put(lcName, protoObject); - - // Do element upgrade. - nsRefPtr list = GetElementsByTagName(lcName); - for (int32_t i = 0; i < list->Length(false); i++) { - nsINode* oldNode = list->Item(i, false); - - // TODO(wchen): Perform upgrade on Shadow DOM when implemented. - // Bug 806506. - nsCOMPtr newNode; - rv = nsNodeUtils::Clone(oldNode, true, getter_AddRefs(newNode)); - NS_ENSURE_SUCCESS(rv, rv); - - nsINode* parentNode = oldNode->GetParentNode(); - MOZ_ASSERT(parentNode, "Node obtained by GetElementsByTagName."); - nsCOMPtr newElement = do_QueryInterface(newNode); - MOZ_ASSERT(newElement, "Cloned of node obtained by GetElementsByTagName."); - - ErrorResult error; - parentNode->ReplaceChild(*newNode, *oldNode, error); - rv = error.ErrorCode(); - NS_ENSURE_SUCCESS(rv, rv); - - // Dispatch elementreplaced to replaced elements. - nsCOMPtr event; - rv = CreateEvent(NS_LITERAL_STRING("elementreplace"), getter_AddRefs(event)); - NS_ENSURE_SUCCESS(rv, rv); - - if (lifecycleCallback) { - // Update static member used for "this" translation - // when calling callback. - sCurrentUpgradeElement = newElement.get(); - lifecycleCallback->Created(nullptr); - sCurrentUpgradeElement = nullptr; - } - - nsCOMPtr ptEvent = do_QueryInterface(event); - MOZ_ASSERT(ptEvent); - - rv = ptEvent->InitElementReplaceEvent(NS_LITERAL_STRING("elementreplace"), - false, false, newElement); - NS_ENSURE_SUCCESS(rv, rv); - - event->SetTrusted(true); - event->SetTarget(oldNode); - nsEventDispatcher::DispatchDOMEvent(oldNode, nullptr, event, - nullptr, nullptr); - } - - nsContentUtils::DispatchTrustedEvent(this, static_cast(this), - NS_LITERAL_STRING("elementupgrade"), - true, true); - - // Create constructor to return. Store the name of the custom element as the - // name of the function. - JSFunction* constructor = JS_NewFunction(aCx, CustomElementConstructor, 0, - JSFUN_CONSTRUCTOR, nullptr, - NS_ConvertUTF16toUTF8(lcName).get()); - JSObject* constructorObject = JS_GetFunctionObject(constructor); - - JS::Value protoVal = OBJECT_TO_JSVAL(protoObject); - NS_ENSURE_TRUE(JS_SetProperty(aCx, constructorObject, "prototype", - &protoVal), NS_ERROR_UNEXPECTED); - - JS::Value constructorVal = OBJECT_TO_JSVAL(constructorObject); - NS_ENSURE_TRUE(JS_SetProperty(aCx, protoObject, "constructor", - &constructorVal), NS_ERROR_UNEXPECTED); - - // Return the constructor. - *aConstructor = OBJECT_TO_JSVAL(constructorObject); - - return NS_OK; -} - already_AddRefed nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI, const nsAString& aLocalName) diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index f050dfea9a32..4c4cb78cdb4a 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1005,18 +1005,6 @@ public: // DocSizeOfIncludingThis is inherited from nsIDocument. virtual nsIDOMNode* AsDOMNode() { return this; } - - static nsIDOMElement* CurrentUpgradeElement() - { - return sCurrentUpgradeElement; - } - - JSObject* GetCustomPrototype(const nsAString& aElementName) - { - JSObject* prototype = nullptr; - mCustomPrototypes.Get(aElementName, &prototype); - return prototype; - } protected: friend class nsNodeUtils; @@ -1174,14 +1162,6 @@ protected: // pop one off this stack, restoring the previous full-screen state nsTArray mFullScreenStack; - // Weak reference to the current upgraded element in document.register. - // This member is used for function |this| translation when calling a - // callback interface in document.register. - static nsIDOMElement* sCurrentUpgradeElement; - - // Hashtable for custom element prototypes in web components. - nsDataHashtable mCustomPrototypes; - nsRefPtr mListenerManager; nsCOMPtr mDOMStyleSheets; nsRefPtr mStyleSheetSetList; diff --git a/content/base/src/nsNodeUtils.h b/content/base/src/nsNodeUtils.h index 1bc36f355e59..15abe70bcfe4 100644 --- a/content/base/src/nsNodeUtils.h +++ b/content/base/src/nsNodeUtils.h @@ -156,16 +156,6 @@ public: nullptr, aNodesWithProperties, nullptr, aResult); } - /** - * Clones aNode, its attributes and, if aDeep is true, its descendant nodes - */ - static nsresult Clone(nsINode *aNode, bool aDeep, nsINode **aResult) - { - nsCOMArray dummyNodeWithProperties; - return CloneAndAdopt(aNode, true, aDeep, nullptr, nullptr, nullptr, - dummyNodeWithProperties, aNode->GetParent(), aResult); - } - /** * Walks aNode, its attributes and descendant nodes. If aNewNodeInfoManager is * not null, it is used to create new nodeinfos for the nodes. Also reparents diff --git a/content/events/src/nsEventDispatcher.cpp b/content/events/src/nsEventDispatcher.cpp index 45321d5b2725..7c4815e65c62 100644 --- a/content/events/src/nsEventDispatcher.cpp +++ b/content/events/src/nsEventDispatcher.cpp @@ -816,8 +816,6 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext, if (aEventType.LowerCaseEqualsLiteral("commandevent") || aEventType.LowerCaseEqualsLiteral("commandevents")) return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nullptr); - if (aEventType.LowerCaseEqualsLiteral("elementreplace")) - return NS_NewDOMElementReplaceEvent(aDOMEvent, aPresContext, nullptr); if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") || aEventType.LowerCaseEqualsLiteral("datacontainerevents")) return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nullptr); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 5efc167917df..578b46015fb1 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -2360,9 +2360,6 @@ nsDOMClassInfo::Init() nsCOMPtr elt = new nsEventListenerThisTranslator(); sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsIDOMEventListener), elt); - nsCOMPtr lct = new nsLifecycleCallbacksThisTranslator(); - sXPConnect->SetFunctionThisTranslator(NS_GET_IID(nsILifecycleCallback), lct); - nsCOMPtr sm = do_GetService("@mozilla.org/scriptsecuritymanager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); @@ -7944,13 +7941,6 @@ nsElementSH::PreCreate(nsISupports *nativeObj, JSContext *cx, } #endif - nsAutoString elementName; - nsContentUtils::ASCIIToLower(element->NodeName(), elementName); - if (StringBeginsWith(elementName, NS_LITERAL_STRING("x-"))) { - // Don't allow slim wrappers for custom elements. - return rv == NS_SUCCESS_ALLOW_SLIM_WRAPPERS ? NS_OK : rv; - } - nsIDocument *doc = element->HasFlag(NODE_FORCE_XBL_BINDINGS) ? element->OwnerDoc() : element->GetCurrentDoc(); @@ -7996,17 +7986,6 @@ nsElementSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } #endif - // If we have a registered x-tag then we fix the prototype. - nsAutoString elementName; - nsContentUtils::ASCIIToLower(element->NodeName(), elementName); - if (StringBeginsWith(elementName, NS_LITERAL_STRING("x-"))) { - nsDocument* document = static_cast(element->OwnerDoc()); - JSObject* prototype = document->GetCustomPrototype(elementName); - if (prototype) { - return JS_SetPrototype(cx, obj, prototype) ? NS_OK : NS_ERROR_UNEXPECTED; - } - } - nsIDocument* doc; if (element->HasFlag(NODE_FORCE_XBL_BINDINGS)) { doc = element->OwnerDoc(); @@ -10218,24 +10197,6 @@ nsEventListenerThisTranslator::TranslateThis(nsISupports *aInitialThis, return NS_OK; } -NS_INTERFACE_MAP_BEGIN(nsLifecycleCallbacksThisTranslator) - NS_INTERFACE_MAP_ENTRY(nsIXPCFunctionThisTranslator) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - - -NS_IMPL_ADDREF(nsLifecycleCallbacksThisTranslator) -NS_IMPL_RELEASE(nsLifecycleCallbacksThisTranslator) - - -NS_IMETHODIMP -nsLifecycleCallbacksThisTranslator::TranslateThis(nsISupports *aInitialThis, - nsISupports **_retval) -{ - NS_IF_ADDREF(*_retval = nsDocument::CurrentUpgradeElement()); - return NS_OK; -} - NS_IMETHODIMP nsDOMConstructorSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj) diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 978b2fe09436..8324487dabfb 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -1208,24 +1208,6 @@ public: NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR }; -class nsLifecycleCallbacksThisTranslator : public nsIXPCFunctionThisTranslator -{ -public: - nsLifecycleCallbacksThisTranslator() - { - } - - virtual ~nsLifecycleCallbacksThisTranslator() - { - } - - // nsISupports - NS_DECL_ISUPPORTS - - // nsIXPCFunctionThisTranslator - NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR -}; - class nsDOMConstructorSH : public nsDOMGenericSH { protected: diff --git a/dom/interfaces/core/nsIDOMDocument.idl b/dom/interfaces/core/nsIDOMDocument.idl index 040a4448e8b0..1922cca74375 100644 --- a/dom/interfaces/core/nsIDOMDocument.idl +++ b/dom/interfaces/core/nsIDOMDocument.idl @@ -47,11 +47,6 @@ interface nsIDOMDocument : nsIDOMNode raises(DOMException); nsIDOMNodeList getElementsByTagName(in DOMString tagname); - [optional_argc, - implicit_jscontext] jsval register(in DOMString name, - [optional] in jsval options) - raises(DOMException); - // Introduced in DOM Level 2: [optional_argc] nsIDOMNode importNode(in nsIDOMNode importedNode, [optional] in boolean deep) @@ -412,24 +407,3 @@ interface nsIDOMDocument : nsIDOMNode */ readonly attribute DOMString compatMode; }; - -/** - * Interface for lifecycle callbacks in document.register. - */ - -[scriptable, function, uuid(bfcd6299-081e-45d8-99f6-cb06fe2e70de)] -interface nsILifecycleCallback : nsISupports -{ - void created(in nsIDOMElement dummy); -}; - -dictionary LifecycleCallbacks { - nsILifecycleCallback created; -}; - -dictionary DocumentRegisterOptions { - nsIVariant prototype; - nsIDOMDocumentFragment customTemplate; - nsIVariant lifecycle; -}; - diff --git a/dom/interfaces/events/Makefile.in b/dom/interfaces/events/Makefile.in index f4f9a22ca0ce..232af1e077bf 100644 --- a/dom/interfaces/events/Makefile.in +++ b/dom/interfaces/events/Makefile.in @@ -47,7 +47,6 @@ XPIDLSRCS = \ nsIDOMUserProximityEvent.idl \ nsIDOMDeviceOrientationEvent.idl \ nsIDOMDeviceMotionEvent.idl \ - nsIDOMElementReplaceEvent.idl \ nsIDOMScrollAreaEvent.idl \ nsIDOMTransitionEvent.idl \ nsIDOMAnimationEvent.idl \ diff --git a/dom/interfaces/events/nsIDOMElementReplaceEvent.idl b/dom/interfaces/events/nsIDOMElementReplaceEvent.idl deleted file mode 100644 index 5be2849ea202..000000000000 --- a/dom/interfaces/events/nsIDOMElementReplaceEvent.idl +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsIDOMEvent.idl" - -interface nsIDOMElement; - -[scriptable, builtinclass, uuid(f57f7c46-d420-4f32-a61b-0eb585d30ee1)] -interface nsIDOMElementReplaceEvent : nsIDOMEvent -{ - readonly attribute nsIDOMElement upgrade; - - void initElementReplaceEvent(in DOMString typeArg, - in boolean canBubbleArg, - in boolean canCancelArg, - in nsIDOMElement upgrade); -}; - -dictionary ElementReplaceEventInit : EventInit -{ - nsIDOMElement upgrade; -}; diff --git a/dom/tests/mochitest/Makefile.in b/dom/tests/mochitest/Makefile.in index fbebe6a84005..5de932179d44 100644 --- a/dom/tests/mochitest/Makefile.in +++ b/dom/tests/mochitest/Makefile.in @@ -27,7 +27,6 @@ DIRS += \ storageevent \ pointerlock \ webapps \ - webcomponents \ $(NULL) #needs IPC support, also tests do not run successfully in Firefox for now diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 2e63d75df237..6bc588a97feb 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -528,8 +528,7 @@ var interfaceNamesInGlobalScope = "RTCPeerConnection", "LocalMediaStream", "CSSConditionRule", - "CSSGroupingRule", - "ElementReplaceEvent" + "CSSGroupingRule" ] for (var i in SpecialPowers.Components.interfaces) { diff --git a/dom/tests/mochitest/webcomponents/Makefile.in b/dom/tests/mochitest/webcomponents/Makefile.in deleted file mode 100644 index aabdefe0590d..000000000000 --- a/dom/tests/mochitest/webcomponents/Makefile.in +++ /dev/null @@ -1,18 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -DEPTH = @DEPTH@ -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = @relativesrcdir@ - -include $(DEPTH)/config/autoconf.mk - -MOCHITEST_FILES = \ - test_document_register.html \ - test_document_register_lifecycle.html \ - $(NULL) - -include $(topsrcdir)/config/rules.mk diff --git a/dom/tests/mochitest/webcomponents/test_document_register.html b/dom/tests/mochitest/webcomponents/test_document_register.html deleted file mode 100644 index 6c23d41ff996..000000000000 --- a/dom/tests/mochitest/webcomponents/test_document_register.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - Test for document.register using custom prototype - - - - - - - -Bug 783129 - -
    -
    - - diff --git a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html b/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html deleted file mode 100644 index 2b93cb992a27..000000000000 --- a/dom/tests/mochitest/webcomponents/test_document_register_lifecycle.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - Test for document.register lifecycle callback - - - - - - - -Bug 783129 - -
    -
    - - diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index 8e23b70748f4..a1fa30d23a93 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -14,8 +14,6 @@ dictionaries = [ [ 'DOMFileMetadataParameters', 'nsIDOMLockedFile.idl' ], [ 'XMLHttpRequestParameters', 'nsIXMLHttpRequest.idl' ], [ 'DeviceStorageEnumerationParameters', 'nsIDOMDeviceStorage.idl' ], - [ 'DocumentRegisterOptions', 'nsIDOMDocument.idl' ], - [ 'LifecycleCallbacks', 'nsIDOMDocument.idl' ], [ 'CameraSize', 'nsIDOMCameraManager.idl' ], [ 'CameraRegion', 'nsIDOMCameraManager.idl' ], [ 'CameraPosition', 'nsIDOMCameraManager.idl' ], @@ -36,6 +34,5 @@ special_includes = [ # name of the type to not include using #include "typename.h" exclude_automatic_type_include = [ 'nsISupports', - 'mozIDOMApplication', - 'nsILifecycleCallback' + 'mozIDOMApplication' ] diff --git a/js/xpconnect/src/event_impl_gen.conf.in b/js/xpconnect/src/event_impl_gen.conf.in index cfc33ba2284d..81d664ff825d 100644 --- a/js/xpconnect/src/event_impl_gen.conf.in +++ b/js/xpconnect/src/event_impl_gen.conf.in @@ -32,7 +32,6 @@ simple_events = [ 'MozWifiConnectionInfoEvent', 'MozCellBroadcastEvent', #endif - 'ElementReplaceEvent', 'DeviceStorageChangeEvent', 'PopupBlockedEvent' ] From 1db359a00866c65d0bd6387f5811676090cd45fa Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Fri, 21 Dec 2012 19:14:09 -0500 Subject: [PATCH 158/217] Back out e01ca7212c8a (bug 654352) for failures in test_bug654352-2 --- content/base/src/Makefile.in | 1 - content/base/src/nsDOMCaretPosition.cpp | 40 ---------------- content/base/src/nsDOMCaretPosition.h | 63 ------------------------ content/base/src/nsDocument.cpp | 62 ------------------------ content/base/test/Makefile.in | 2 - content/base/test/test_bug654352-2.html | 64 ------------------------- content/base/test/test_bug654352.html | 50 ------------------- dom/bindings/Bindings.conf | 4 -- dom/interfaces/core/nsIDOMDocument.idl | 15 +----- dom/webidl/CaretPosition.webidl | 12 ----- dom/webidl/WebIDL.mk | 1 - 11 files changed, 2 insertions(+), 312 deletions(-) delete mode 100644 content/base/src/nsDOMCaretPosition.cpp delete mode 100644 content/base/src/nsDOMCaretPosition.h delete mode 100644 content/base/test/test_bug654352-2.html delete mode 100644 content/base/test/test_bug654352.html delete mode 100644 dom/webidl/CaretPosition.webidl diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index 29a4cb1dbe33..c9684b58c9fb 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -74,7 +74,6 @@ CPPSRCS = \ nsDOMAttribute.cpp \ nsDOMAttributeMap.cpp \ nsDOMBlobBuilder.cpp \ - nsDOMCaretPosition.cpp \ nsDOMDocumentType.cpp \ nsDOMFile.cpp \ nsDOMFileReader.cpp \ diff --git a/content/base/src/nsDOMCaretPosition.cpp b/content/base/src/nsDOMCaretPosition.cpp deleted file mode 100644 index 5a287d94103a..000000000000 --- a/content/base/src/nsDOMCaretPosition.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsDOMCaretPosition.h" -#include "mozilla/dom/CaretPositionBinding.h" -#include "nsContentUtils.h" - -nsDOMCaretPosition::nsDOMCaretPosition(nsINode* aNode, uint32_t aOffset) - : mOffset(aOffset), mOffsetNode(aNode) -{ - SetIsDOMBinding(); -} - -nsDOMCaretPosition::~nsDOMCaretPosition() -{ -} - -nsINode* nsDOMCaretPosition::GetOffsetNode() const -{ - return mOffsetNode; -} - -JSObject* -nsDOMCaretPosition::WrapObject(JSContext *aCx, JSObject *aScope, - bool *aTried) -{ - return mozilla::dom::CaretPositionBinding::Wrap(aCx, aScope, this, aTried); -} - -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCaretPosition, mOffsetNode) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCaretPosition) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCaretPosition) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCaretPosition) - NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - diff --git a/content/base/src/nsDOMCaretPosition.h b/content/base/src/nsDOMCaretPosition.h deleted file mode 100644 index 88b6df67e75f..000000000000 --- a/content/base/src/nsDOMCaretPosition.h +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsDOMCaretPosition_h -#define nsDOMCaretPosition_h - -#include "nsCycleCollectionParticipant.h" -#include "nsCOMPtr.h" -#include "nsINode.h" -#include "nsWrapperCache.h" - -/** - * Implementation of a DOM Caret Position, which is a node and offset within - * that node, in the DOM tree. - * - * http://www.w3.org/TR/cssom-view/#dom-documentview-caretrangefrompoint - * - * @see Document::mozCaretPositionFromPoint(float x, float y) - */ -class nsDOMCaretPosition : public nsISupports, - public nsWrapperCache -{ -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCaretPosition) - - nsDOMCaretPosition(nsINode* aNode, uint32_t aOffset); - - /** - * Retrieve the offset (character position within the DOM node) of the - * CaretPosition. - * - * @returns The offset within the DOM node. - */ - uint32_t Offset() const { return mOffset; } - - /** - * Retrieve the DOM node with which this CaretPosition was established. - * Normally, this will be created from a point, so it will be the DOM - * node that lies at the point specified. - * - * @returns The DOM node of the CaretPosition. - * - * @see Document::mozCaretPositionFromPoint(float x, float y) - */ - nsINode* GetOffsetNode() const; - - nsISupports* GetParentObject() const - { - return GetOffsetNode(); - } - - virtual JSObject* WrapObject(JSContext *aCx, JSObject *aScope, bool *aTried) - MOZ_OVERRIDE MOZ_FINAL; - -protected: - virtual ~nsDOMCaretPosition(); - uint32_t mOffset; - nsCOMPtr mOffsetNode; -}; -#endif - diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 8bc2d0fd7f77..23bd89c0e7ac 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -177,10 +177,6 @@ #include "nsSandboxFlags.h" #include "nsIAppsService.h" -#include "nsFrame.h" -#include "nsDOMCaretPosition.h" -#include "nsIDOMHTMLTextAreaElement.h" - using namespace mozilla; using namespace mozilla::dom; @@ -8401,64 +8397,6 @@ ResetFullScreen(nsIDocument* aDocument, void* aData) return true; } -NS_IMETHODIMP -nsDocument::MozCaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos) -{ - NS_ENSURE_ARG_POINTER(aCaretPos); - *aCaretPos = nullptr; - - nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); - nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); - nsPoint pt(x, y); - - nsIPresShell *ps = GetShell(); - if (!ps) { - return NS_OK; - } - - nsIFrame *rootFrame = ps->GetRootFrame(); - - // XUL docs, unlike HTML, have no frame tree until everything's done loading - if (!rootFrame) { - return NS_OK; // return null to premature XUL callers as a reminder to wait - } - - nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, true, - false); - if (!ptFrame) { - return NS_OK; - } - - // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need - // to adjust to frame-relative coordinates before we can perform this call. - // It should also not take into account the padding of the frame. - nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame); - - nsFrame::ContentOffsets offsets = - ptFrame->GetContentOffsetsFromPoint(adjustedPoint); - - nsCOMPtr node = offsets.content; - uint32_t offset = offsets.offset; - if (node && node->IsInNativeAnonymousSubtree()) { - nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent(); - nsCOMPtr input = do_QueryInterface(nonanon); - nsCOMPtr textArea = do_QueryInterface(nonanon); - bool isText; - if (textArea || (input && - NS_SUCCEEDED(input->MozIsTextField(false, &isText)) && - isText)) { - node = nonanon; - } else { - node = nullptr; - offset = 0; - } - } - - *aCaretPos = new nsDOMCaretPosition(node, offset); - NS_ADDREF(*aCaretPos); - return NS_OK; -} - /* static */ void nsDocument::ExitFullScreen() diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 5d10b02acaf6..e756d86fc2ba 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -503,8 +503,6 @@ MOCHITEST_FILES_B = \ file_html_in_xhr3.html \ file_html_in_xhr.sjs \ test_bug647518.html \ - test_bug654352.html \ - test_bug654352-2.html \ test_bug664916.html \ test_bug666604.html \ test_bug675121.html \ diff --git a/content/base/test/test_bug654352-2.html b/content/base/test/test_bug654352-2.html deleted file mode 100644 index ff57b21d804c..000000000000 --- a/content/base/test/test_bug654352-2.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - Test for Bug 654352 - - - - -
    abc, abc, abc
    -abc, abc, abc
    -

    -marquee -
    - - diff --git a/content/base/test/test_bug654352.html b/content/base/test/test_bug654352.html deleted file mode 100644 index f0b383c5af5c..000000000000 --- a/content/base/test/test_bug654352.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - Test for Bug 654352 - - - - - -Mozilla Bug 654352 -

    - -
    -
    -
    - -
    test text
    -
    -
    -
    - - - - - diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 7df5626a88b2..bbc5c6022f86 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -180,10 +180,6 @@ DOMInterfaces = { 'skipGen': True }], -'CaretPosition' : { - 'nativeType': 'nsDOMCaretPosition', -}, - 'DOMParser': { 'nativeType': 'nsDOMParser', }, diff --git a/dom/interfaces/core/nsIDOMDocument.idl b/dom/interfaces/core/nsIDOMDocument.idl index 1922cca74375..8bfc6ba833a1 100644 --- a/dom/interfaces/core/nsIDOMDocument.idl +++ b/dom/interfaces/core/nsIDOMDocument.idl @@ -27,7 +27,7 @@ interface nsIDOMLocation; * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html */ -[scriptable, uuid(d19897dc-948a-42e7-8ac6-d8a0bd141b85)] +[scriptable, uuid(31ce7ae7-15d5-4fc8-912b-ae0e23e93146)] interface nsIDOMDocument : nsIDOMNode { readonly attribute nsIDOMDocumentType doctype; @@ -325,7 +325,7 @@ interface nsIDOMDocument : nsIDOMNode */ void mozSetImageElement(in DOMString aImageElementId, in nsIDOMElement aImageElement); - + /** * Element which is currently the full-screen element as per the DOM * full-screen api. @@ -367,17 +367,6 @@ interface nsIDOMDocument : nsIDOMNode */ readonly attribute nsIDOMElement mozPointerLockElement; - /** - * Retrieve the location of the caret position (DOM node and character - * offset within that node), given a point. - * - * @param x Horizontal point at which to determine the caret position, in - * page coordinates. - * @param y Vertical point at which to determine the caret position, in - * page coordinates. - */ - nsISupports /* CaretPosition */ mozCaretPositionFromPoint(in float x, in float y); - /** * Exit pointer is lock if locked, as per the DOM pointer lock api. * diff --git a/dom/webidl/CaretPosition.webidl b/dom/webidl/CaretPosition.webidl deleted file mode 100644 index 4cc42759f25e..000000000000 --- a/dom/webidl/CaretPosition.webidl +++ /dev/null @@ -1,12 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -interface CaretPosition { - - /** - * The offsetNode could potentially be null due to anonymous content. - */ - readonly attribute Node? offsetNode; - readonly attribute unsigned long offset; -}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 8bee83d33bdb..5ed6f4027983 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -26,7 +26,6 @@ webidl_files = \ CSSValue.webidl \ CSSValueList.webidl \ DelayNode.webidl \ - CaretPosition.webidl \ DOMImplementation.webidl \ DOMParser.webidl \ DOMSettableTokenList.webidl \ From e9c46e4563f169db5905d1efd418599a18281a65 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 22 Dec 2012 09:15:43 +0900 Subject: [PATCH 159/217] Bug 795542 - Part 1: Create Text(En|De)coderBase. r=bent --HG-- rename : dom/encoding/TextDecoder.h => dom/encoding/TextDecoderBase.h rename : dom/encoding/TextEncoder.h => dom/encoding/TextEncoderBase.h --- dom/encoding/Makefile.in | 2 + dom/encoding/TextDecoder.cpp | 19 ++++----- dom/encoding/TextDecoder.h | 66 +++++------------------------ dom/encoding/TextDecoderBase.h | 76 ++++++++++++++++++++++++++++++++++ dom/encoding/TextEncoder.cpp | 18 ++++---- dom/encoding/TextEncoder.h | 62 ++++++++------------------- dom/encoding/TextEncoderBase.h | 72 ++++++++++++++++++++++++++++++++ 7 files changed, 195 insertions(+), 120 deletions(-) create mode 100644 dom/encoding/TextDecoderBase.h create mode 100644 dom/encoding/TextEncoderBase.h diff --git a/dom/encoding/Makefile.in b/dom/encoding/Makefile.in index 915320467cdf..a5bf9c73a18a 100644 --- a/dom/encoding/Makefile.in +++ b/dom/encoding/Makefile.in @@ -24,7 +24,9 @@ EXPORTS_NAMESPACES = mozilla/dom EXPORTS_mozilla/dom = \ EncodingUtils.h \ TextDecoder.h \ + TextDecoderBase.h \ TextEncoder.h \ + TextEncoderBase.h \ $(NULL) CPPSRCS = \ diff --git a/dom/encoding/TextDecoder.cpp b/dom/encoding/TextDecoder.cpp index f53f49acab53..eedbbac4d3fb 100644 --- a/dom/encoding/TextDecoder.cpp +++ b/dom/encoding/TextDecoder.cpp @@ -14,9 +14,8 @@ namespace dom { static const PRUnichar kReplacementChar = static_cast(0xFFFD); void -TextDecoder::Init(const nsAString& aEncoding, - const TextDecoderOptions& aFatal, - ErrorResult& aRv) +TextDecoderBase::Init(const nsAString& aEncoding, const bool aFatal, + ErrorResult& aRv) { nsAutoString label(aEncoding); EncodingUtils::TrimSpaceCharacters(label); @@ -31,7 +30,7 @@ TextDecoder::Init(const nsAString& aEncoding, // If the constructor is called with an options argument, // and the fatal property of the dictionary is set, // set the internal fatal flag of the decoder object. - mFatal = aFatal.mFatal; + mFatal = aFatal; // Create a decoder object for mEncoding. nsCOMPtr ccm = @@ -53,10 +52,10 @@ TextDecoder::Init(const nsAString& aEncoding, } void -TextDecoder::Decode(const ArrayBufferView* aView, - const TextDecodeOptions& aOptions, - nsAString& aOutDecodedString, - ErrorResult& aRv) +TextDecoderBase::Decode(const ArrayBufferView* aView, + const bool aStream, + nsAString& aOutDecodedString, + ErrorResult& aRv) { const char* data; int32_t length; @@ -94,7 +93,7 @@ TextDecoder::Decode(const ArrayBufferView* aView, // If the internal streaming flag of the decoder object is not set, // then reset the encoding algorithm state to the default values - if (!aOptions.mStream) { + if (!aStream) { mDecoder->Reset(); if (rv == NS_OK_UDEC_MOREINPUT) { if (mFatal) { @@ -113,7 +112,7 @@ TextDecoder::Decode(const ArrayBufferView* aView, } void -TextDecoder::GetEncoding(nsAString& aEncoding) +TextDecoderBase::GetEncoding(nsAString& aEncoding) { CopyASCIItoUTF16(mEncoding, aEncoding); nsContentUtils::ASCIIToLower(aEncoding); diff --git a/dom/encoding/TextDecoder.h b/dom/encoding/TextDecoder.h index 7dd6a8e49e46..6b94575ce693 100644 --- a/dom/encoding/TextDecoder.h +++ b/dom/encoding/TextDecoder.h @@ -5,21 +5,14 @@ #ifndef mozilla_dom_textdecoder_h_ #define mozilla_dom_textdecoder_h_ -#include "jsapi.h" -#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/TextDecoderBase.h" #include "mozilla/dom/TextDecoderBinding.h" -#include "mozilla/dom/TypedArray.h" -#include "mozilla/ErrorResult.h" -#include "nsIUnicodeDecoder.h" -#include "nsString.h" - -#include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" namespace mozilla { namespace dom { -class TextDecoder : public nsISupports, public nsWrapperCache +class TextDecoder MOZ_FINAL + : public nsISupports, public nsWrapperCache, public TextDecoderBase { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -29,11 +22,11 @@ public: static already_AddRefed Constructor(nsISupports* aGlobal, const nsAString& aEncoding, - const TextDecoderOptions& aFatal, + const TextDecoderOptions& aOptions, ErrorResult& aRv) { nsRefPtr txtDecoder = new TextDecoder(aGlobal); - txtDecoder->Init(aEncoding, aFatal, aRv); + txtDecoder->Init(aEncoding, aOptions.mFatal, aRv); if (aRv.Failed()) { return nullptr; } @@ -41,7 +34,7 @@ public: } TextDecoder(nsISupports* aGlobal) - : mGlobal(aGlobal), mFatal(false) + : mGlobal(aGlobal) { MOZ_ASSERT(aGlobal); SetIsDOMBinding(); @@ -52,7 +45,7 @@ public: {} virtual JSObject* - WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap) + WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap) MOZ_OVERRIDE { return TextDecoderBinding::Wrap(aCx, aScope, this, aTriedToWrap); } @@ -63,53 +56,16 @@ public: return mGlobal; } - /** - * Return the encoding name. - * - * @param aEncoding, current encoding. - */ - void GetEncoding(nsAString& aEncoding); - - /** - * Decodes incoming byte stream of characters in charset indicated by - * encoding. - * - * The encoding algorithm state is reset if aOptions.stream is not set. - * - * If the fatal flag is set then a decoding error will throw EncodingError. - * Else the decoder will return a decoded string with replacement - * character(s) for unidentified character(s). - * - * @param aView, incoming byte stream of characters to be decoded to - * to UTF-16 code points. - * @param aOptions, indicates if streaming or not. - * @param aOutDecodedString, decoded string of UTF-16 code points. - * @param aRv, error result. - */ void Decode(const ArrayBufferView* aView, const TextDecodeOptions& aOptions, nsAString& aOutDecodedString, - ErrorResult& aRv); + ErrorResult& aRv) { + return TextDecoderBase::Decode(aView, aOptions.mStream, + aOutDecodedString, aRv); + } private: - nsCString mEncoding; - nsCOMPtr mDecoder; nsCOMPtr mGlobal; - bool mFatal; - - /** - * Validates provided encoding and throws an exception if invalid encoding. - * If no encoding is provided then mEncoding is default initialised to "utf-8". - * - * @param aEncoding Optional encoding (case insensitive) provided. - * Default value is "utf-8" if no encoding is provided. - * @param aFatal aFatal, indicates whether to throw an 'EncodingError' - * exception or not. - * @return aRv EncodingError exception else null. - */ - void Init(const nsAString& aEncoding, - const TextDecoderOptions& aFatal, - ErrorResult& aRv); }; } // dom diff --git a/dom/encoding/TextDecoderBase.h b/dom/encoding/TextDecoderBase.h new file mode 100644 index 000000000000..4937f31618e8 --- /dev/null +++ b/dom/encoding/TextDecoderBase.h @@ -0,0 +1,76 @@ +/* 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/. */ + +#ifndef mozilla_dom_textdecoderbase_h_ +#define mozilla_dom_textdecoderbase_h_ + +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/TypedArray.h" +#include "nsIUnicodeDecoder.h" + +namespace mozilla { +class ErrorResult; + +namespace dom { + +class TextDecoderBase +{ +protected: + TextDecoderBase() + : mFatal(false) + {} + + virtual + ~TextDecoderBase() + {} + + /** + * Validates provided encoding and throws an exception if invalid encoding. + * If no encoding is provided then mEncoding is default initialised to "utf-8". + * + * @param aEncoding Optional encoding (case insensitive) provided. + * Default value is "utf-8" if no encoding is provided. + * @param aFatal aFatal, indicates whether to throw an 'EncodingError' + * exception or not. + * @return aRv EncodingError exception else null. + */ + void Init(const nsAString& aEncoding, const bool aFatal, ErrorResult& aRv); + +public: + /** + * Return the encoding name. + * + * @param aEncoding, current encoding. + */ + void GetEncoding(nsAString& aEncoding); + + /** + * Decodes incoming byte stream of characters in charset indicated by + * encoding. + * + * The encoding algorithm state is reset if aOptions.mStream is not set. + * + * If the fatal flag is set then a decoding error will throw EncodingError. + * Else the decoder will return a decoded string with replacement + * character(s) for unidentified character(s). + * + * @param aView, incoming byte stream of characters to be decoded to + * to UTF-16 code points. + * @param aOptions, indicates if streaming or not. + * @param aOutDecodedString, decoded string of UTF-16 code points. + * @param aRv, error result. + */ + void Decode(const ArrayBufferView* aView, const bool aStream, + nsAString& aOutDecodedString, ErrorResult& aRv); + +private: + nsCString mEncoding; + nsCOMPtr mDecoder; + bool mFatal; +}; + +} // dom +} // mozilla + +#endif // mozilla_dom_textdecoderbase_h_ diff --git a/dom/encoding/TextEncoder.cpp b/dom/encoding/TextEncoder.cpp index 82cad68c8669..b9dfc4a22507 100644 --- a/dom/encoding/TextEncoder.cpp +++ b/dom/encoding/TextEncoder.cpp @@ -12,8 +12,7 @@ namespace mozilla { namespace dom { void -TextEncoder::Init(const nsAString& aEncoding, - ErrorResult& aRv) +TextEncoderBase::Init(const nsAString& aEncoding, ErrorResult& aRv) { nsAutoString label(aEncoding); EncodingUtils::TrimSpaceCharacters(label); @@ -49,10 +48,10 @@ TextEncoder::Init(const nsAString& aEncoding, } JSObject* -TextEncoder::Encode(JSContext* aCx, - const nsAString& aString, - const TextEncodeOptions& aOptions, - ErrorResult& aRv) +TextEncoderBase::Encode(JSContext* aCx, + const nsAString& aString, + const bool aStream, + ErrorResult& aRv) { // Run the steps of the encoding algorithm. int32_t srcLen = aString.Length(); @@ -77,7 +76,7 @@ TextEncoder::Encode(JSContext* aCx, // If the internal streaming flag is not set, then reset // the encoding algorithm state to the default values for encoding. - if (!aOptions.mStream) { + if (!aStream) { int32_t finishLen = maxLen - dstLen; rv = mEncoder->Finish(buf + dstLen, &finishLen); if (NS_SUCCEEDED(rv)) { @@ -88,8 +87,7 @@ TextEncoder::Encode(JSContext* aCx, JSObject* outView = nullptr; if (NS_SUCCEEDED(rv)) { buf[dstLen] = '\0'; - outView = Uint8Array::Create(aCx, this, dstLen, - reinterpret_cast(buf.get())); + outView = CreateUint8Array(aCx, buf, dstLen); if (!outView) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; @@ -103,7 +101,7 @@ TextEncoder::Encode(JSContext* aCx, } void -TextEncoder::GetEncoding(nsAString& aEncoding) +TextEncoderBase::GetEncoding(nsAString& aEncoding) { CopyASCIItoUTF16(mEncoding, aEncoding); nsContentUtils::ASCIIToLower(aEncoding); diff --git a/dom/encoding/TextEncoder.h b/dom/encoding/TextEncoder.h index fa2a5f7f9ad3..da5273177fe9 100644 --- a/dom/encoding/TextEncoder.h +++ b/dom/encoding/TextEncoder.h @@ -5,21 +5,14 @@ #ifndef mozilla_dom_textencoder_h_ #define mozilla_dom_textencoder_h_ -#include "jsapi.h" -#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/TextEncoderBase.h" #include "mozilla/dom/TextEncoderBinding.h" -#include "mozilla/dom/TypedArray.h" -#include "mozilla/ErrorResult.h" -#include "nsIUnicodeEncoder.h" -#include "nsString.h" - -#include "nsCOMPtr.h" -#include "nsCycleCollectionParticipant.h" namespace mozilla { namespace dom { -class TextEncoder : public nsISupports, public nsWrapperCache +class TextEncoder MOZ_FINAL + : public nsISupports, public nsWrapperCache, public TextEncoderBase { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -51,7 +44,7 @@ public: {} virtual JSObject* - WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap) + WrapObject(JSContext* aCx, JSObject* aScope, bool* aTriedToWrap) MOZ_OVERRIDE { return TextEncoderBinding::Wrap(aCx, aScope, this, aTriedToWrap); } @@ -62,44 +55,23 @@ public: return mGlobal; } - /** - * Return the encoding name. - * - * @param aEncoding, current encoding. - */ - void GetEncoding(nsAString& aEncoding); - - /** - * Encodes incoming utf-16 code units/ DOM string to the requested encoding. - * - * @param aCx Javascript context. - * @param aString utf-16 code units to be encoded. - * @param aOptions Streaming option. Initialised by default to false. - * If the streaming option is false, then the encoding - * algorithm state will get reset. If set to true then - * the previous encoding is reused/continued. - * @return JSObject* The Uint8Array wrapped in a JS object. - */ JSObject* Encode(JSContext* aCx, const nsAString& aString, const TextEncodeOptions& aOptions, - ErrorResult& aRv); -private: - nsCString mEncoding; - nsCOMPtr mEncoder; - nsCOMPtr mGlobal; + ErrorResult& aRv) { + return TextEncoderBase::Encode(aCx, aString, aOptions.mStream, aRv); + } - /** - * Validates provided encoding and throws an exception if invalid encoding. - * If no encoding is provided then mEncoding is default initialised to "utf-8". - * - * @param aEncoding Optional encoding (case insensitive) provided. - * (valid values are "utf-8", "utf-16", "utf-16be") - * Default value is "utf-8" if no encoding is provided. - * @return aRv EncodingError exception else null. - */ - void Init(const nsAString& aEncoding, - ErrorResult& aRv); +protected: + virtual JSObject* + CreateUint8Array(JSContext* aCx, char* aBuf, uint32_t aLen) MOZ_OVERRIDE + { + return Uint8Array::Create(aCx, this, aLen, + reinterpret_cast(aBuf)); + } + +private: + nsCOMPtr mGlobal; }; } // dom diff --git a/dom/encoding/TextEncoderBase.h b/dom/encoding/TextEncoderBase.h new file mode 100644 index 000000000000..ceab10171c8e --- /dev/null +++ b/dom/encoding/TextEncoderBase.h @@ -0,0 +1,72 @@ +/* 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/. */ + +#ifndef mozilla_dom_textencoderbase_h_ +#define mozilla_dom_textencoderbase_h_ + +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/TypedArray.h" +#include "nsIUnicodeEncoder.h" + +namespace mozilla { +class ErrorResult; + +namespace dom { + +class TextEncoderBase +{ +protected: + TextEncoderBase() + {} + + virtual + ~TextEncoderBase() + {} + + /** + * Validates provided encoding and throws an exception if invalid encoding. + * If no encoding is provided then mEncoding is default initialised to "utf-8". + * + * @param aEncoding Optional encoding (case insensitive) provided. + * (valid values are "utf-8", "utf-16", "utf-16be") + * Default value is "utf-8" if no encoding is provided. + * @return aRv EncodingError exception else null. + */ + void Init(const nsAString& aEncoding, ErrorResult& aRv); + +public: + /** + * Return the encoding name. + * + * @param aEncoding, current encoding. + */ + void GetEncoding(nsAString& aEncoding); + + /** + * Encodes incoming utf-16 code units/ DOM string to the requested encoding. + * + * @param aCx Javascript context. + * @param aString utf-16 code units to be encoded. + * @param aOptions Streaming option. Initialised by default to false. + * If the streaming option is false, then the encoding + * algorithm state will get reset. If set to true then + * the previous encoding is reused/continued. + * @return JSObject* The Uint8Array wrapped in a JS object. + */ + JSObject* Encode(JSContext* aCx, const nsAString& aString, + const bool aStream, ErrorResult& aRv); + +protected: + virtual JSObject* + CreateUint8Array(JSContext* aCx, char* aBuf, uint32_t aLen) = 0; + +private: + nsCString mEncoding; + nsCOMPtr mEncoder; +}; + +} // dom +} // mozilla + +#endif // mozilla_dom_textencoderbase_h_ From 139459454e24d89cc36fd4f16e7bc2f8a66da55f Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 22 Dec 2012 09:16:14 +0900 Subject: [PATCH 160/217] Bug 795542 - Part 2: Implement StringEncoding API objects in Workers. r=bent --- dom/bindings/Bindings.conf | 12 +++++- dom/encoding/test/Makefile.in | 1 + dom/encoding/test/test_TextDecoder.html | 6 ++- dom/encoding/test/test_TextEncoder.html | 6 ++- dom/encoding/test/worker_helper.js | 48 +++++++++++++++++++++ dom/workers/DOMBindingInlines.h | 6 +++ dom/workers/Makefile.in | 4 ++ dom/workers/TextDecoder.cpp | 44 +++++++++++++++++++ dom/workers/TextDecoder.h | 52 ++++++++++++++++++++++ dom/workers/TextEncoder.cpp | 42 ++++++++++++++++++ dom/workers/TextEncoder.h | 57 +++++++++++++++++++++++++ dom/workers/WorkerScope.cpp | 4 ++ 12 files changed, 279 insertions(+), 3 deletions(-) create mode 100644 dom/encoding/test/worker_helper.js create mode 100644 dom/workers/TextDecoder.cpp create mode 100644 dom/workers/TextDecoder.h create mode 100644 dom/workers/TextEncoder.cpp create mode 100644 dom/workers/TextEncoder.h diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index bbc5c6022f86..6d76af984348 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -446,9 +446,19 @@ DOMInterfaces = { 'resultNotAddRefed': [ 'getItem' ] }, -'TextEncoder': { +'TextDecoder': [ +{ + 'workers': True, +}], + +'TextEncoder': [ +{ 'implicitJSContext': [ 'encode' ], }, +{ + 'workers': True, + 'implicitJSContext': [ 'encode' ], +}], 'URL' : { 'concrete': False, diff --git a/dom/encoding/test/Makefile.in b/dom/encoding/test/Makefile.in index 33ec0b612422..ff0ee722ce54 100644 --- a/dom/encoding/test/Makefile.in +++ b/dom/encoding/test/Makefile.in @@ -30,6 +30,7 @@ MOCHITEST_FILES = \ test_stringencoding.html \ test_submit_euckr.html \ test_utf16_files.html \ + worker_helper.js \ $(NULL) MOCHITEST_CHROME_FILES = \ diff --git a/dom/encoding/test/test_TextDecoder.html b/dom/encoding/test/test_TextDecoder.html index 784748e2be61..320db32d9688 100644 --- a/dom/encoding/test/test_TextDecoder.html +++ b/dom/encoding/test/test_TextDecoder.html @@ -2,16 +2,18 @@ - Test for Bug 764234 + Test for TextDecoder +
    diff --git a/dom/encoding/test/test_TextEncoder.html b/dom/encoding/test/test_TextEncoder.html index 10b9b733c78f..b0c32fbdbf16 100644 --- a/dom/encoding/test/test_TextEncoder.html +++ b/dom/encoding/test/test_TextEncoder.html @@ -2,15 +2,17 @@ - Test for Bug 764234 + Test for TextEncoder +
    diff --git a/dom/encoding/test/worker_helper.js b/dom/encoding/test/worker_helper.js new file mode 100644 index 000000000000..bfa408e6a8e0 --- /dev/null +++ b/dom/encoding/test/worker_helper.js @@ -0,0 +1,48 @@ +/* + * worker_helper.js + * bug 764234 tests +*/ +function runTestInWorker(files) { + function workerRun() { + var tests = []; + var asserts; + test = function(func, msg) { + asserts = []; + tests.push({asserts: asserts, msg: msg}); + } + assert_equals = function(result, expected, msg) { + asserts.push(["assert_equals", result, expected, msg]); + }; + assert_true = function(condition, msg) { + asserts.push(["assert_true", condition, msg]); + }; + assert_unreached = function(condition, msg) { + asserts.push(["assert_unreached", condition, msg]); + }; + onmessage = function(event) { + importScripts.apply(self, event.data); + runTest(); + postMessage(tests); + }; + } + + var url = URL.createObjectURL(new Blob([ + runTest.toString(), "\n\n", + "(", workerRun.toString(), ")();" + ])); + var worker = new Worker(url); + var base = location.toString().replace(/\/[^\/]*$/,"/"); + worker.postMessage(files.map(function(f) { return base + f; })); + worker.onmessage = function(event) { + URL.revokeObjectURL(url); + event.data.forEach(function(t) { + test(function() { + t.asserts.forEach(function(a) { + func = a.shift(); + self[func].apply(self, a); + }); + }, "worker " + t.msg); + }); + done(); + }; +} diff --git a/dom/workers/DOMBindingInlines.h b/dom/workers/DOMBindingInlines.h index 9a2585d77d5e..f7594b0e745a 100644 --- a/dom/workers/DOMBindingInlines.h +++ b/dom/workers/DOMBindingInlines.h @@ -7,12 +7,16 @@ #define mozilla_dom_workers_dombindinginlines_h__ #include "mozilla/dom/FileReaderSyncBinding.h" +#include "mozilla/dom/TextDecoderBinding.h" +#include "mozilla/dom/TextEncoderBinding.h" #include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/dom/XMLHttpRequestUploadBinding.h" BEGIN_WORKERS_NAMESPACE class FileReaderSync; +class TextDecoder; +class TextEncoder; class XMLHttpRequest; class XMLHttpRequestUpload; @@ -44,6 +48,8 @@ struct WrapPrototypeTraits }; SPECIALIZE_PROTO_TRAITS(FileReaderSync) +SPECIALIZE_PROTO_TRAITS(TextDecoder) +SPECIALIZE_PROTO_TRAITS(TextEncoder) SPECIALIZE_PROTO_TRAITS(XMLHttpRequest) SPECIALIZE_PROTO_TRAITS(XMLHttpRequestUpload) diff --git a/dom/workers/Makefile.in b/dom/workers/Makefile.in index 4d090b777785..c430abf631b7 100644 --- a/dom/workers/Makefile.in +++ b/dom/workers/Makefile.in @@ -30,6 +30,8 @@ CPPSRCS = \ Principal.cpp \ RuntimeService.cpp \ ScriptLoader.cpp \ + TextDecoder.cpp \ + TextEncoder.cpp \ Worker.cpp \ WorkerPrivate.cpp \ WorkerScope.cpp \ @@ -52,6 +54,8 @@ EXPORTS_mozilla/dom/workers/bindings = \ EventListenerManager.h \ EventTarget.h \ FileReaderSync.h \ + TextDecoder.h \ + TextEncoder.h \ WorkerFeature.h \ XMLHttpRequestEventTarget.h \ XMLHttpRequestUpload.h \ diff --git a/dom/workers/TextDecoder.cpp b/dom/workers/TextDecoder.cpp new file mode 100644 index 000000000000..e13f5f410585 --- /dev/null +++ b/dom/workers/TextDecoder.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TextDecoder.h" +#include "DOMBindingInlines.h" + +USING_WORKERS_NAMESPACE +using mozilla::ErrorResult; +using mozilla::dom::TextDecoderOptionsWorkers; + +void +TextDecoder::_trace(JSTracer* aTrc) +{ + DOMBindingBase::_trace(aTrc); +} + +void +TextDecoder::_finalize(JSFreeOp* aFop) +{ + DOMBindingBase::_finalize(aFop); +} + +// static +TextDecoder* +TextDecoder::Constructor(JSContext* aCx, JSObject* aObj, + const nsAString& aEncoding, + const TextDecoderOptionsWorkers& aOptions, + ErrorResult& aRv) +{ + nsRefPtr txtDecoder = new TextDecoder(aCx); + txtDecoder->Init(aEncoding, aOptions.mFatal, aRv); + if (aRv.Failed()) { + return nullptr; + } + + if (!Wrap(aCx, aObj, txtDecoder)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return txtDecoder; +} diff --git a/dom/workers/TextDecoder.h b/dom/workers/TextDecoder.h new file mode 100644 index 000000000000..e0a867055f7f --- /dev/null +++ b/dom/workers/TextDecoder.h @@ -0,0 +1,52 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* 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/. */ + +#ifndef mozilla_dom_workers_textdecoder_h_ +#define mozilla_dom_workers_textdecoder_h_ + +#include "mozilla/dom/TextDecoderBase.h" +#include "mozilla/dom/workers/bindings/DOMBindingBase.h" +#include "mozilla/dom/TextDecoderBinding.h" + +BEGIN_WORKERS_NAMESPACE + +class TextDecoder MOZ_FINAL : public DOMBindingBase, + public TextDecoderBase +{ +protected: + TextDecoder(JSContext* aCx) + : DOMBindingBase(aCx) + {} + + virtual + ~TextDecoder() + {} + +public: + virtual void + _trace(JSTracer* aTrc) MOZ_OVERRIDE; + + virtual void + _finalize(JSFreeOp* aFop) MOZ_OVERRIDE; + + static TextDecoder* + Constructor(JSContext* aCx, JSObject* aObj, + const nsAString& aEncoding, + const TextDecoderOptionsWorkers& aOptions, + ErrorResult& aRv); + + void + Decode(const ArrayBufferView* aView, + const TextDecodeOptionsWorkers& aOptions, + nsAString& aOutDecodedString, + ErrorResult& aRv) { + return TextDecoderBase::Decode(aView, aOptions.mStream, + aOutDecodedString, aRv); + } +}; + +END_WORKERS_NAMESPACE + +#endif // mozilla_dom_workers_textdecoder_h_ diff --git a/dom/workers/TextEncoder.cpp b/dom/workers/TextEncoder.cpp new file mode 100644 index 000000000000..6d9141227975 --- /dev/null +++ b/dom/workers/TextEncoder.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TextEncoder.h" +#include "DOMBindingInlines.h" + +USING_WORKERS_NAMESPACE +using mozilla::ErrorResult; + +void +TextEncoder::_trace(JSTracer* aTrc) +{ + DOMBindingBase::_trace(aTrc); +} + +void +TextEncoder::_finalize(JSFreeOp* aFop) +{ + DOMBindingBase::_finalize(aFop); +} + +// static +TextEncoder* +TextEncoder::Constructor(JSContext* aCx, JSObject* aObj, + const nsAString& aEncoding, + ErrorResult& aRv) +{ + nsRefPtr txtEncoder = new TextEncoder(aCx); + txtEncoder->Init(aEncoding, aRv); + if (aRv.Failed()) { + return nullptr; + } + + if (!Wrap(aCx, aObj, txtEncoder)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return txtEncoder; +} diff --git a/dom/workers/TextEncoder.h b/dom/workers/TextEncoder.h new file mode 100644 index 000000000000..b8cbc4c2385d --- /dev/null +++ b/dom/workers/TextEncoder.h @@ -0,0 +1,57 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* 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/. */ + +#ifndef mozilla_dom_workers_textencoder_h_ +#define mozilla_dom_workers_textencoder_h_ + +#include "mozilla/dom/TextEncoderBase.h" +#include "mozilla/dom/workers/bindings/DOMBindingBase.h" +#include "mozilla/dom/TextEncoderBinding.h" + +BEGIN_WORKERS_NAMESPACE + +class TextEncoder MOZ_FINAL : public DOMBindingBase, + public TextEncoderBase +{ +protected: + TextEncoder(JSContext* aCx) + : DOMBindingBase(aCx) + {} + + virtual + ~TextEncoder() + {} + + virtual JSObject* + CreateUint8Array(JSContext* aCx, char* aBuf, uint32_t aLen) MOZ_OVERRIDE + { + return Uint8Array::Create(aCx, this, aLen, + reinterpret_cast(aBuf)); + } + +public: + virtual void + _trace(JSTracer* aTrc) MOZ_OVERRIDE; + + virtual void + _finalize(JSFreeOp* aFop) MOZ_OVERRIDE; + + static TextEncoder* + Constructor(JSContext* aCx, JSObject* aObj, + const nsAString& aEncoding, + ErrorResult& aRv); + + JSObject* + Encode(JSContext* aCx, + const nsAString& aString, + const TextEncodeOptionsWorkers& aOptions, + ErrorResult& aRv) { + return TextEncoderBase::Encode(aCx, aString, aOptions.mStream, aRv); + } +}; + +END_WORKERS_NAMESPACE + +#endif // mozilla_dom_workers_textencoder_h_ diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 2a0e131a41e8..addb62695c90 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -13,6 +13,8 @@ #include "mozilla/dom/EventTargetBinding.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/FileReaderSyncBinding.h" +#include "mozilla/dom/TextDecoderBinding.h" +#include "mozilla/dom/TextEncoderBinding.h" #include "mozilla/dom/XMLHttpRequestBinding.h" #include "mozilla/dom/XMLHttpRequestUploadBinding.h" #include "mozilla/OSFileConstants.h" @@ -983,6 +985,8 @@ CreateDedicatedWorkerGlobalScope(JSContext* aCx) // Init other paris-bindings. if (!FileReaderSyncBinding_workers::GetConstructorObject(aCx, global) || + !TextDecoderBinding_workers::GetConstructorObject(aCx, global) || + !TextEncoderBinding_workers::GetConstructorObject(aCx, global) || !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, global) || !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, global)) { return NULL; From 9595813922e6a0c8064ced8c81d70e8f47bd798a Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Sat, 22 Dec 2012 09:16:50 +0900 Subject: [PATCH 161/217] Bug 823080 - Fix warning spam on MSVC. r=sphink --- js/src/gc/Barrier.h | 2 +- js/src/gc/Marking.h | 6 +++--- js/src/ion/IonCaches.h | 4 ++-- js/src/ion/IonCode.h | 2 +- js/src/ion/IonFrameIterator.h | 4 ++-- js/src/ion/IonFrames.h | 4 ++-- js/src/ion/JSONSpewer.h | 2 +- js/src/jsanalyze.h | 2 +- js/src/jsfriendapi.h | 3 ++- js/src/jsfun.h | 7 ++++--- js/src/jsgc.h | 2 +- js/src/jsgcinlines.h | 2 +- js/src/jsinfer.h | 2 +- js/src/jsobj.h | 4 ++-- js/src/jsopcode.h | 2 +- js/src/jspropertytree.h | 2 +- js/src/jsprvtd.h | 2 +- js/src/jspubtd.h | 8 +++++--- js/src/jsscope.h | 8 ++++---- js/src/jsscript.h | 9 ++++----- js/src/vm/Debugger.h | 2 +- js/src/vm/ObjectImpl.h | 4 ++-- js/src/vm/SPSProfiler.h | 2 +- js/src/vm/ThreadPool.h | 2 +- 24 files changed, 45 insertions(+), 42 deletions(-) diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index 26b4d425b5f3..2ef03263f51c 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -320,7 +320,7 @@ BarrieredSetPair(JSCompartment *comp, v2.post(); } -struct Shape; +class Shape; class BaseShape; namespace types { struct TypeObject; } diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 4ee79e16aa52..072623dbaddf 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -18,9 +18,9 @@ extern "C" { struct JSContext; -struct JSFunction; +class JSFunction; struct JSObject; -struct JSScript; +class JSScript; } class JSAtom; @@ -32,7 +32,7 @@ class ArgumentsObject; class BaseShape; class GlobalObject; class UnownedBaseShape; -struct Shape; +class Shape; template class HeapPtr; diff --git a/js/src/ion/IonCaches.h b/js/src/ion/IonCaches.h index 26b1a5ee5666..6a2555580095 100644 --- a/js/src/ion/IonCaches.h +++ b/js/src/ion/IonCaches.h @@ -12,8 +12,8 @@ #include "TypeOracle.h" #include "Registers.h" -struct JSFunction; -struct JSScript; +class JSFunction; +class JSScript; namespace js { namespace ion { diff --git a/js/src/ion/IonCode.h b/js/src/ion/IonCode.h index c8bf896d8e49..be1c3955ba98 100644 --- a/js/src/ion/IonCode.h +++ b/js/src/ion/IonCode.h @@ -18,7 +18,7 @@ namespace JSC { class ExecutablePool; } -struct JSScript; +class JSScript; namespace js { namespace ion { diff --git a/js/src/ion/IonFrameIterator.h b/js/src/ion/IonFrameIterator.h index 5b2a32a97280..0ef0081481cc 100644 --- a/js/src/ion/IonFrameIterator.h +++ b/js/src/ion/IonFrameIterator.h @@ -12,8 +12,8 @@ #include "IonCode.h" #include "SnapshotReader.h" -struct JSFunction; -struct JSScript; +class JSFunction; +class JSScript; namespace js { namespace ion { diff --git a/js/src/ion/IonFrames.h b/js/src/ion/IonFrames.h index 1b0c3b279dc6..b1061721205a 100644 --- a/js/src/ion/IonFrames.h +++ b/js/src/ion/IonFrames.h @@ -17,8 +17,8 @@ #include "IonCode.h" #include "IonFrameIterator.h" -struct JSFunction; -struct JSScript; +class JSFunction; +class JSScript; namespace js { namespace ion { diff --git a/js/src/ion/JSONSpewer.h b/js/src/ion/JSONSpewer.h index af16cc4361ea..3e84d6500c59 100644 --- a/js/src/ion/JSONSpewer.h +++ b/js/src/ion/JSONSpewer.h @@ -13,7 +13,7 @@ #include "gc/Root.h" #include "jsscript.h" -struct JSScript; +class JSScript; namespace js { namespace ion { diff --git a/js/src/jsanalyze.h b/js/src/jsanalyze.h index 8f79d0208e5f..050bc4c477f5 100644 --- a/js/src/jsanalyze.h +++ b/js/src/jsanalyze.h @@ -19,7 +19,7 @@ #include "js/TemplateLib.h" #include "vm/ScopeObject.h" -struct JSScript; +class JSScript; /* Forward declaration of downstream register allocations computed for join points. */ namespace js { namespace mjit { struct RegisterAllocation; } } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 172b2e988697..2428009002c5 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -320,7 +320,8 @@ struct BaseShape { JSObject *parent; }; -struct Shape { +class Shape { +public: shadow::BaseShape *base; jsid _1; uint32_t slotInfo; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 6e09c07b1753..4310e5e2cc95 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -21,8 +21,9 @@ ForwardDeclareJS(Script); namespace js { class FunctionExtended; } -struct JSFunction : public JSObject +class JSFunction : public JSObject { + public: enum Flags { INTERPRETED = 0x0001, /* function has a JSScript and environment. */ NATIVE_CTOR = 0x0002, /* native that can be called as a constructor */ @@ -58,7 +59,7 @@ struct JSFunction : public JSObject uint16_t flags; /* bitfield composed of the above Flags enum */ union U { class Native { - friend struct JSFunction; + friend class JSFunction; js::Native native; /* native method pointer or null */ const JSJitInfo *jitinfo; /* Information about this function to be used by the JIT; @@ -335,7 +336,7 @@ namespace js { */ class FunctionExtended : public JSFunction { - friend struct JSFunction; + friend class JSFunction; /* Reserved slots available for storage by particular native functions. */ HeapValue extendedSlots[2]; diff --git a/js/src/jsgc.h b/js/src/jsgc.h index f9215db54423..683c34b56d51 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -34,7 +34,7 @@ struct JSCompartment; namespace js { class GCHelperThread; -struct Shape; +class Shape; struct SliceBudget; enum HeapState { diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index a608e212e4fa..6a425dfdc2e0 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -22,7 +22,7 @@ using JS::AssertCanGC; namespace js { -struct Shape; +class Shape; /* * This auto class should be used around any code that might cause a mark bit to diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 7406fdc62b0c..087e68939840 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -1114,7 +1114,7 @@ struct TypeCallsite /* Persistent type information for a script, retained across GCs. */ class TypeScript { - friend struct ::JSScript; + friend class ::JSScript; /* Analysis information for the script, cleared on each GC. */ analyze::ScriptAnalysis *analysis; diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 02adba6af735..4c11f89cb8d4 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -271,7 +271,7 @@ class WithObject; struct JSObject : public js::ObjectImpl { private: - friend struct js::Shape; + friend class js::Shape; friend struct js::GCMarker; friend class js::NewObjectCache; @@ -657,7 +657,7 @@ struct JSObject : public js::ObjectImpl * Function-specific getters and setters. */ - friend struct JSFunction; + friend class JSFunction; inline JSFunction *toFunction(); inline const JSFunction *toFunction() const; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 057754fb310a..7b6657ef52d6 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -525,7 +525,7 @@ IsSetterPC(jsbytecode *pc) */ class PCCounts { - friend struct ::JSScript; + friend class ::JSScript; double *counts; #ifdef DEBUG size_t capacity; diff --git a/js/src/jspropertytree.h b/js/src/jspropertytree.h index a538b935b475..5008016021f5 100644 --- a/js/src/jspropertytree.h +++ b/js/src/jspropertytree.h @@ -67,7 +67,7 @@ class KidsPointer { class PropertyTree { - friend struct ::JSFunction; + friend class ::JSFunction; JSCompartment *compartment; diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 64763ddb7922..5da066244dc7 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -134,7 +134,7 @@ class InlineMap; class LifoAlloc; -struct Shape; +class Shape; class Breakpoint; class BreakpointSite; diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 8e2df8132b4a..0edd6fbc7b35 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -172,7 +172,7 @@ typedef enum { JSTRACE_LAST = JSTRACE_TYPE_OBJECT } JSGCTraceKind; -/* Struct typedefs. */ +/* Struct typedefs and class forward declarations. */ typedef struct JSClass JSClass; typedef struct JSCompartment JSCompartment; typedef struct JSConstDoubleSpec JSConstDoubleSpec; @@ -180,7 +180,6 @@ typedef struct JSContext JSContext; typedef struct JSCrossCompartmentCall JSCrossCompartmentCall; typedef struct JSErrorReport JSErrorReport; typedef struct JSExceptionState JSExceptionState; -typedef struct JSFunction JSFunction; typedef struct JSFunctionSpec JSFunctionSpec; typedef struct JSIdArray JSIdArray; typedef struct JSLocaleCallbacks JSLocaleCallbacks; @@ -193,7 +192,6 @@ typedef struct JSPropertySpec JSPropertySpec; typedef struct JSRuntime JSRuntime; typedef struct JSSecurityCallbacks JSSecurityCallbacks; typedef struct JSStackFrame JSStackFrame; -typedef struct JSScript JSScript; typedef struct JSStructuredCloneCallbacks JSStructuredCloneCallbacks; typedef struct JSStructuredCloneReader JSStructuredCloneReader; typedef struct JSStructuredCloneWriter JSStructuredCloneWriter; @@ -201,10 +199,14 @@ typedef struct JSTracer JSTracer; #ifdef __cplusplus class JSFlatString; +class JSFunction; +class JSScript; class JSStableString; // long story class JSString; #else typedef struct JSFlatString JSFlatString; +typedef struct JSFunction JSFunction; +typedef struct JSScript JSScript; typedef struct JSString JSString; #endif /* !__cplusplus */ diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 1a0ec6c4990d..693d048eace9 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -230,7 +230,7 @@ struct StackBaseShape; class BaseShape : public js::gc::Cell { public: - friend struct Shape; + friend class Shape; friend struct StackBaseShape; friend struct StackShape; @@ -438,10 +438,10 @@ typedef HashSet, StackBaseShape, SystemAllocPolicy> BaseShapeSet; -struct Shape : public js::gc::Cell +class Shape : public js::gc::Cell { friend struct ::JSObject; - friend struct ::JSFunction; + friend class ::JSFunction; friend class js::Bindings; friend class js::ObjectImpl; friend class js::PropertyTree; @@ -549,7 +549,7 @@ struct Shape : public js::gc::Cell class Range { protected: - friend struct Shape; + friend class Shape; /* |cursor| is rooted manually when necessary using Range::AutoRooter. */ RawShape cursor; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index b4bb74bcca3c..80a7e5a72298 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -30,7 +30,7 @@ namespace ion { # define ION_DISABLED_SCRIPT ((js::ion::IonScript *)0x1) # define ION_COMPILING_SCRIPT ((js::ion::IonScript *)0x2) -struct Shape; +class Shape; class BindingIter; @@ -216,7 +216,7 @@ struct RootMethods { class ScriptCounts { - friend struct ::JSScript; + friend class ::JSScript; friend struct ScriptAndCounts; /* @@ -247,7 +247,7 @@ typedef HashMap class BreakpointSite { friend class Breakpoint; friend struct ::JSCompartment; - friend struct ::JSScript; + friend class ::JSScript; friend class Debugger; public: diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 41f3aade817e..edd559aa821a 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -916,7 +916,7 @@ extern HeapSlot *emptyObjectElements; struct Class; struct GCMarker; struct ObjectOps; -struct Shape; +class Shape; class NewObjectCache; @@ -1060,7 +1060,7 @@ class ObjectImpl : public gc::Cell protected: friend struct GCMarker; - friend struct Shape; + friend class Shape; friend class NewObjectCache; inline void invalidateSlotRange(uint32_t start, uint32_t count); diff --git a/js/src/vm/SPSProfiler.h b/js/src/vm/SPSProfiler.h index cf3822dd5228..91d1a9adc232 100644 --- a/js/src/vm/SPSProfiler.h +++ b/js/src/vm/SPSProfiler.h @@ -103,7 +103,7 @@ * from a signal handler when the JIT code is executing. */ -struct JSFunction; +class JSFunction; namespace js { diff --git a/js/src/vm/ThreadPool.h b/js/src/vm/ThreadPool.h index 45c2c0dba5c6..4578e85d4be8 100644 --- a/js/src/vm/ThreadPool.h +++ b/js/src/vm/ThreadPool.h @@ -22,7 +22,7 @@ struct JSContext; struct JSRuntime; struct JSCompartment; -struct JSScript; +class JSScript; namespace js { From ac06f32c7599016e5b5a7f46cb721b493254636f Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 19 Dec 2012 16:49:30 -0500 Subject: [PATCH 162/217] Bug 823283 - Don't query for JSRESOLVE_QUALIFIED when determining whether to check for an undeclared variable, when possibly adding a fast-path expando to the global object for assignment to a non-existent property. Instead examine the current bytecode to see if it's an undeclared variable access. r=bz, r=luke --HG-- rename : dom/encoding/TextDecoderBase.h => dom/encoding/TextDecoder.h rename : dom/encoding/TextEncoderBase.h => dom/encoding/TextEncoder.h extra : rebase_source : aa656c52edc95cd95bb109dbfe2c40d8303dbe34 --- dom/base/nsDOMClassInfo.cpp | 22 +++++------ js/src/frontend/BytecodeEmitter.cpp | 2 + js/src/jsfriendapi.h | 11 +++--- js/src/jsobj.cpp | 59 +++++++++++++++++++++++++++-- 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 578b46015fb1..5eb1cc20a60c 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -7368,20 +7368,18 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, } } - // Define a fast expando, the key here is to use JS_PropertyStub - // as the getter/setter, which makes us stay out of XPConnect - // when using this property. + // Define a fast expando. The key here is to use JS_PropertyStub as the + // getter/setter, which makes us stay out of XPConnect when using this + // property. // - // We don't need to worry about property attributes here as we - // know here we're dealing with an undefined property set, so - // we're not declaring readonly or permanent properties. + // We're adding a new property here, so we don't need to worry about + // conflicting with any existing ones. // - // Since we always create the undeclared property here without given a - // chance for the interpreter to report applicable strict mode warnings, - // we must take care to check those warnings here. - JSString *str = JSID_TO_STRING(id); - if ((!(flags & JSRESOLVE_QUALIFIED) && - !js::CheckUndeclaredVarAssignment(cx, str)) || + // Since we always create the undeclared property here, shortcutting the + // normal process, we go out of our way to tell the JS engine to report + // strict warnings/errors using js::ReportIfUndeclaredVarAssignment. + js::Rooted str(cx, JSID_TO_STRING(id)); + if (!js::ReportIfUndeclaredVarAssignment(cx, str) || !::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE)) { *_retval = JS_FALSE; diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 6f94172201f6..94c921ba2b2d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1184,6 +1184,8 @@ TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op) !pn->isDeoptimized() && !bce->sc->strict) { + // If you change anything here, you might also need to change + // js::CheckUndeclaredVarAssignment. switch (*op) { case JSOP_NAME: *op = JSOP_GETGNAME; break; case JSOP_SETNAME: *op = JSOP_SETGNAME; break; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 2428009002c5..4d66c77cecf1 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -238,13 +238,14 @@ extern JS_FRIEND_API(bool) IsAtomsCompartment(const JSCompartment *c); /* - * Check whether it is OK to assign an undeclared property with name - * propname of the global object in the current script on cx. Reports - * an error if one needs to be reported (in particular in all cases - * when it returns false). + * Check whether it is OK to assign an undeclared variable with the name + * |propname| at the current location in script. It is not an error if there is + * no current script location, or if that location is not an assignment to an + * undeclared variable. Reports an error if one needs to be reported (and, + * particularly, always reports when it returns false). */ extern JS_FRIEND_API(bool) -CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname); +ReportIfUndeclaredVarAssignment(JSContext *cx, HandleString propname); struct WeakMapTracer; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 2fe7a9dd74e1..14f86ec766d1 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3474,8 +3474,8 @@ js::GetMethod(JSContext *cx, HandleObject obj, HandleId id, unsigned getHow, Mut return op(cx, obj, obj, id, vp); } -JS_FRIEND_API(bool) -js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname) +static bool +MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname) { { UnrootedScript script = cx->stack.currentScript(NULL, ContextStack::ALLOW_CROSS_COMPARTMENT); @@ -3496,6 +3496,56 @@ js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname) JSMSG_UNDECLARED_VAR, bytes.ptr()); } +bool +js::ReportIfUndeclaredVarAssignment(JSContext *cx, HandleString propname) +{ + { + jsbytecode *pc; + UnrootedScript script = cx->stack.currentScript(&pc, ContextStack::ALLOW_CROSS_COMPARTMENT); + if (!script) + return true; + + /* If neither cx nor the code is strict, then no check is needed. */ + if (!script->strict && !cx->hasStrictOption()) + return true; + + /* + * We only need to check for bare name mutations: we shouldn't be + * warning, or throwing, or whatever, if we're not doing a variable + * access. + * + * TryConvertToGname in frontend/BytecodeEmitter.cpp checks for rather + * more opcodes when it does, in the normal course of events, what this + * method does in the abnormal course of events. Because we're called + * in narrower circumstances, we only need check two. We don't need to + * check for the increment/decrement opcodes because they're no-ops: + * the actual semantics are implemented by desugaring. And we don't + * need to check name-access because this method is only supposed to be + * called in assignment contexts. + */ + MOZ_ASSERT(*pc != JSOP_INCNAME); + MOZ_ASSERT(*pc != JSOP_INCGNAME); + MOZ_ASSERT(*pc != JSOP_NAMEINC); + MOZ_ASSERT(*pc != JSOP_GNAMEINC); + MOZ_ASSERT(*pc != JSOP_DECNAME); + MOZ_ASSERT(*pc != JSOP_DECGNAME); + MOZ_ASSERT(*pc != JSOP_NAMEDEC); + MOZ_ASSERT(*pc != JSOP_GNAMEDEC); + MOZ_ASSERT(*pc != JSOP_NAME); + MOZ_ASSERT(*pc != JSOP_GETGNAME); + if (*pc != JSOP_SETNAME && *pc != JSOP_SETGNAME) + return true; + } + + JSAutoByteString bytes(cx, propname); + return !!bytes && + JS_ReportErrorFlagsAndNumber(cx, + JSREPORT_WARNING | JSREPORT_STRICT | + JSREPORT_STRICT_MODE_ERROR, + js_GetErrorMessage, NULL, + JSMSG_UNDECLARED_VAR, bytes.ptr()); +} + bool JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report) { @@ -3586,8 +3636,9 @@ baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receive if (obj->isGlobal() && (defineHow & DNP_UNQUALIFIED) && - !js::CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) { - return JS_FALSE; + !MaybeReportUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) + { + return false; } } From 372242a94332351665a4a7fe7b59f66bdef59128 Mon Sep 17 00:00:00 2001 From: Sean Stangl Date: Fri, 14 Dec 2012 16:07:35 -0800 Subject: [PATCH 163/217] Bug 820124, Part 1/2 - Use RegExpShared for lazy RegExpStatics. r=dvander --- js/src/builtin/RegExp.cpp | 8 +++---- js/src/gc/Marking.cpp | 1 - js/src/gc/Marking.h | 1 - js/src/gc/RootMarking.cpp | 3 --- js/src/vm/RegExpObject.cpp | 12 ++++++++++ js/src/vm/RegExpObject.h | 20 ++++++++++++---- js/src/vm/RegExpStatics-inl.h | 43 ++++++++++++++++++----------------- js/src/vm/RegExpStatics.cpp | 11 ++++----- js/src/vm/RegExpStatics.h | 18 +++++++-------- 9 files changed, 66 insertions(+), 51 deletions(-) diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 7155ba2ccb59..8ec5eb5556fb 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -114,7 +114,7 @@ js::CreateRegExpMatchResult(JSContext *cx, HandleString string, MatchPairs &matc } RegExpRunStatus -ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, RegExpObject ®exp, +ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, JSLinearString *input, StableCharPtr chars, size_t length, size_t *lastIndex, MatchConduit &matches) { @@ -126,7 +126,7 @@ ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, RegExpShared &re, RegExpObj /* Only one MatchPair slot provided: execute short-circuiting regexp. */ status = re.executeMatchOnly(cx, chars, length, lastIndex, *matches.u.pair); if (status == RegExpRunStatus_Success && res) - res->updateLazily(cx, input, ®exp, lastIndex_orig); + res->updateLazily(cx, input, &re, lastIndex_orig); } else { /* Vector of MatchPairs provided: execute full regexp. */ status = re.execute(cx, chars, length, lastIndex, *matches.u.pairs); @@ -151,7 +151,7 @@ js::ExecuteRegExpLegacy(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, MatchConduit conduit(&matches); RegExpRunStatus status = - ExecuteRegExpImpl(cx, res, *shared, reobj, input, chars, length, lastIndex, conduit); + ExecuteRegExpImpl(cx, res, *shared, input, chars, length, lastIndex, conduit); if (status == RegExpRunStatus_Error) return false; @@ -591,7 +591,7 @@ js::ExecuteRegExp(JSContext *cx, HandleObject regexp, HandleString string, Match /* Steps 8-21. */ size_t lastIndexInt(i); RegExpRunStatus status = - ExecuteRegExpImpl(cx, res, *re, *reobj, stableInput, chars, length, &lastIndexInt, matches); + ExecuteRegExpImpl(cx, res, *re, stableInput, chars, length, &lastIndexInt, matches); if (status == RegExpRunStatus_Error) return RegExpRunStatus_Error; diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp index 524c88890096..e88a31413379 100644 --- a/js/src/gc/Marking.cpp +++ b/js/src/gc/Marking.cpp @@ -332,7 +332,6 @@ DeclMarkerImpl(Object, DebugScopeObject) DeclMarkerImpl(Object, GlobalObject) DeclMarkerImpl(Object, JSObject) DeclMarkerImpl(Object, JSFunction) -DeclMarkerImpl(Object, RegExpObject) DeclMarkerImpl(Object, ScopeObject) DeclMarkerImpl(Script, JSScript) DeclMarkerImpl(Shape, Shape) diff --git a/js/src/gc/Marking.h b/js/src/gc/Marking.h index 072623dbaddf..a11f2ddfdf35 100644 --- a/js/src/gc/Marking.h +++ b/js/src/gc/Marking.h @@ -97,7 +97,6 @@ DeclMarker(Object, DebugScopeObject) DeclMarker(Object, GlobalObject) DeclMarker(Object, JSObject) DeclMarker(Object, JSFunction) -DeclMarker(Object, RegExpObject) DeclMarker(Object, ScopeObject) DeclMarker(Script, JSScript) DeclMarker(Shape, Shape) diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index f2142e7ec801..0ea4a45130ae 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -674,9 +674,6 @@ Shape::Range::AutoRooter::trace(JSTracer *trc) void RegExpStatics::AutoRooter::trace(JSTracer *trc) { - if (statics->regexp) - MarkObjectRoot(trc, reinterpret_cast(&statics->regexp), - "RegExpStatics::AutoRooter regexp"); if (statics->matchesInput) MarkStringRoot(trc, reinterpret_cast(&statics->matchesInput), "RegExpStatics::AutoRooter matchesInput"); diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index e2d9a786a7c7..50cbb36324b9 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -645,6 +645,18 @@ RegExpCompartment::RegExpCompartment(JSRuntime *rt) RegExpCompartment::~RegExpCompartment() { JS_ASSERT(map_.empty()); + + /* + * RegExpStatics may have prevented a single RegExpShared from + * being collected during RegExpCompartment::sweep(). + */ + if (!inUse_.empty()) { + PendingSet::Enum e(inUse_); + RegExpShared *shared = e.front(); + JS_ASSERT(shared->activeUseCount == 0); + js_delete(shared); + e.removeFront(); + } JS_ASSERT(inUse_.empty()); } diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index b2607a1509f5..f3d70e4b0b7a 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -198,22 +198,31 @@ class RegExpShared class RegExpGuard { RegExpShared *re_; + + private: RegExpGuard(const RegExpGuard &) MOZ_DELETE; void operator=(const RegExpGuard &) MOZ_DELETE; + public: RegExpGuard() : re_(NULL) {} RegExpGuard(RegExpShared &re) : re_(&re) { re_->incRef(); } + ~RegExpGuard() { release(); } + + public: void init(RegExpShared &re) { - JS_ASSERT(!re_); + JS_ASSERT(!initialized()); re_ = &re; re_->incRef(); } - ~RegExpGuard() { - if (re_) + void release() { + if (re_) { re_->decRef(); + re_ = NULL; + } } + bool initialized() const { return !!re_; } RegExpShared *re() const { JS_ASSERT(initialized()); return re_; } RegExpShared *operator->() { return re(); } @@ -225,9 +234,12 @@ class RegExpCompartment struct Key { JSAtom *atom; uint16_t flag; + Key() {} Key(JSAtom *atom, RegExpFlag flag) - : atom(atom), flag(flag) {} + : atom(atom), flag(flag) + { } + typedef Key Lookup; static HashNumber hash(const Lookup &l) { return DefaultHasher::hash(l.atom) ^ (l.flag << 1); diff --git a/js/src/vm/RegExpStatics-inl.h b/js/src/vm/RegExpStatics-inl.h index 239b67c782f7..49eba7424ddd 100644 --- a/js/src/vm/RegExpStatics-inl.h +++ b/js/src/vm/RegExpStatics-inl.h @@ -27,15 +27,6 @@ SizeOfRegExpStaticsData(const JSObject *obj, JSMallocSizeOfFun mallocSizeOf) return mallocSizeOf(obj->getPrivate()); } -inline -RegExpStatics::RegExpStatics() - : pendingLazyEvaluation(false), - bufferLink(NULL), - copied(false) -{ - clear(); -} - inline bool RegExpStatics::createDependent(JSContext *cx, size_t start, size_t end, Value *out) { @@ -231,15 +222,18 @@ RegExpStatics::copyTo(RegExpStatics &dst) if (!pendingLazyEvaluation) dst.matches.initArrayFrom(matches); + if (regexpGuard.initialized()) + dst.regexpGuard.init(*regexpGuard); + dst.matchesInput = matchesInput; - dst.regexp = regexp; dst.lastIndex = lastIndex; dst.pendingInput = pendingInput; dst.flags = flags; dst.pendingLazyEvaluation = pendingLazyEvaluation; - JS_ASSERT_IF(pendingLazyEvaluation, regexp); + JS_ASSERT_IF(pendingLazyEvaluation, regexpGuard.initialized()); JS_ASSERT_IF(pendingLazyEvaluation, matchesInput); + JS_ASSERT(regexpGuard.initialized() == dst.regexpGuard.initialized()); } inline void @@ -261,17 +255,20 @@ RegExpStatics::restore() inline void RegExpStatics::updateLazily(JSContext *cx, JSLinearString *input, - RegExpObject *regexp, size_t lastIndex) + RegExpShared *shared, size_t lastIndex) { - JS_ASSERT(input && regexp); + JS_ASSERT(input && shared); aboutToWrite(); BarrieredSetPair(cx->compartment, pendingInput, input, matchesInput, input); - pendingLazyEvaluation = true; - this->regexp = regexp; + if (regexpGuard.initialized()) + regexpGuard.release(); + regexpGuard.init(*shared); + this->lastIndex = lastIndex; + pendingLazyEvaluation = true; } inline bool @@ -282,7 +279,7 @@ RegExpStatics::updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchP /* Unset all lazy state. */ pendingLazyEvaluation = false; - this->regexp = NULL; + this->regexpGuard.release(); this->lastIndex = size_t(-1); BarrieredSetPair(cx->compartment, @@ -301,11 +298,14 @@ inline void RegExpStatics::clear() { aboutToWrite(); - flags = RegExpFlag(0); - pendingInput = NULL; - pendingLazyEvaluation = false; - matchesInput = NULL; + matches.forgetArray(); + matchesInput = NULL; + regexpGuard.release(); + lastIndex = size_t(-1); + pendingInput = NULL; + flags = RegExpFlag(0); + pendingLazyEvaluation = false; } inline void @@ -363,8 +363,9 @@ RegExpStatics::checkInvariants() { #ifdef DEBUG if (pendingLazyEvaluation) { - JS_ASSERT(regexp); + JS_ASSERT(regexpGuard.initialized()); JS_ASSERT(pendingInput); + JS_ASSERT(lastIndex != size_t(-1)); return; } diff --git a/js/src/vm/RegExpStatics.cpp b/js/src/vm/RegExpStatics.cpp index 3834c02e03a9..e87557045c9a 100644 --- a/js/src/vm/RegExpStatics.cpp +++ b/js/src/vm/RegExpStatics.cpp @@ -74,7 +74,7 @@ RegExpStatics::executeLazy(JSContext *cx) if (!pendingLazyEvaluation) return true; - JS_ASSERT(regexp); + JS_ASSERT(regexpGuard.initialized()); JS_ASSERT(matchesInput); JS_ASSERT(lastIndex != size_t(-1)); @@ -87,17 +87,14 @@ RegExpStatics::executeLazy(JSContext *cx) StableCharPtr chars(matchesInput->chars(), length); /* Execute the full regular expression. */ - RegExpGuard shared; - if (!regexp->getShared(cx, &shared)) - return false; - - RegExpRunStatus status = shared->execute(cx, chars, length, &this->lastIndex, this->matches); + RegExpRunStatus status = regexpGuard->execute(cx, chars, length, &this->lastIndex, this->matches); if (status == RegExpRunStatus_Error) return false; /* Unset lazy state and remove rooted values that now have no use. */ pendingLazyEvaluation = false; - regexp = NULL; + regexpGuard.release(); + lastIndex = size_t(-1); return true; } diff --git a/js/src/vm/RegExpStatics.h b/js/src/vm/RegExpStatics.h index aa742ccf6a3a..9a6a7e0c3e4c 100644 --- a/js/src/vm/RegExpStatics.h +++ b/js/src/vm/RegExpStatics.h @@ -15,6 +15,7 @@ #include "js/Vector.h" #include "vm/MatchPairs.h" +#include "vm/RegExpObject.h" namespace js { @@ -25,7 +26,7 @@ class RegExpStatics HeapPtr matchesInput; /* The previous RegExp input, used to resolve lazy state. */ - HeapPtr regexp; + RegExpGuard regexpGuard; /* Strong reference to RegExpShared. */ size_t lastIndex; /* The latest RegExp input, set before execution. */ @@ -33,7 +34,7 @@ class RegExpStatics RegExpFlag flags; /* - * If true, |matchesInput|, |regexp|, and |lastIndex| may be used + * If true, |matchesInput|, |regexpGuard|, and |lastIndex| may be used * to replay the last executed RegExp, and |matches| is invalid. */ bool pendingLazyEvaluation; @@ -42,6 +43,10 @@ class RegExpStatics RegExpStatics *bufferLink; bool copied; + public: + RegExpStatics() : bufferLink(NULL), copied(false) { clear(); } + static JSObject *create(JSContext *cx, GlobalObject *parent); + private: bool executeLazy(JSContext *cx); @@ -78,14 +83,9 @@ class RegExpStatics friend class PreserveRegExpStatics; public: - inline RegExpStatics(); - - static JSObject *create(JSContext *cx, GlobalObject *parent); - /* Mutators. */ - inline void updateLazily(JSContext *cx, JSLinearString *input, - RegExpObject *regexp, size_t lastIndex); + RegExpShared *shared, size_t lastIndex); inline bool updateFromMatchPairs(JSContext *cx, JSLinearString *input, MatchPairs &newPairs); inline void setMultiline(JSContext *cx, bool enabled); @@ -118,8 +118,6 @@ class RegExpStatics } void mark(JSTracer *trc) { - if (regexp) - gc::MarkObject(trc, ®exp, "res->regexp"); if (pendingInput) MarkString(trc, &pendingInput, "res->pendingInput"); if (matchesInput) From 1e81321e2e40fb962a6e9004fc08adf029d2522b Mon Sep 17 00:00:00 2001 From: Dave Camp Date: Tue, 18 Dec 2012 17:14:00 +0100 Subject: [PATCH 164/217] Bug 796006 - Don't fully expand nodes with large amounts of children in markup panel. r=jwalker --- browser/devtools/markupview/MarkupView.jsm | 200 +++++++++++++++--- browser/devtools/markupview/markup-view.xhtml | 2 + browser/devtools/markupview/test/Makefile.in | 4 +- .../test/browser_inspector_markup_subset.html | 32 +++ .../test/browser_inspector_markup_subset.js | 145 +++++++++++++ .../browser/devtools/inspector.properties | 6 + .../gnomestripe/devtools/markup-view.css | 4 + .../themes/pinstripe/devtools/markup-view.css | 4 + .../themes/winstripe/devtools/markup-view.css | 4 + 9 files changed, 374 insertions(+), 27 deletions(-) create mode 100644 browser/devtools/markupview/test/browser_inspector_markup_subset.html create mode 100644 browser/devtools/markupview/test/browser_inspector_markup_subset.js diff --git a/browser/devtools/markupview/MarkupView.jsm b/browser/devtools/markupview/MarkupView.jsm index 8d533e941ebb..a9ab5f2c95c8 100644 --- a/browser/devtools/markupview/MarkupView.jsm +++ b/browser/devtools/markupview/MarkupView.jsm @@ -12,6 +12,7 @@ const Ci = Components.interfaces; const PAGE_SIZE = 10; const PREVIEW_AREA = 700; +const DEFAULT_MAX_CHILDREN = 100; this.EXPORTED_SYMBOLS = ["MarkupView"]; @@ -20,6 +21,7 @@ Cu.import("resource:///modules/devtools/CssRuleView.jsm"); Cu.import("resource:///modules/devtools/Templater.jsm"); Cu.import("resource:///modules/devtools/Undo.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); /** * Vocabulary for the purposes of this file: @@ -46,6 +48,12 @@ this.MarkupView = function MarkupView(aInspector, aFrame, aControllerWindow) this.doc = this._frame.contentDocument; this._elt = this.doc.querySelector("#root"); + try { + this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize"); + } catch(ex) { + this.maxChildren = DEFAULT_MAX_CHILDREN; + } + this.undo = new UndoStack(); this.undo.installController(aControllerWindow); @@ -69,7 +77,7 @@ this.MarkupView = function MarkupView(aInspector, aFrame, aControllerWindow) MarkupView.prototype = { _selectedContainer: null, - template: function MT_template(aName, aDest, aOptions) + template: function MT_template(aName, aDest, aOptions={stack: "markup-view.xhtml"}) { let node = this.doc.getElementById("template-" + aName).cloneNode(true); node.removeAttribute("id"); @@ -288,7 +296,6 @@ MarkupView.prototype = { let walker = documentWalker(aNode); let parent = walker.parentNode(); if (parent) { - // Make sure parents of this node are imported too. var container = new MarkupContainer(this, aNode); } else { var container = new RootContainer(this, aNode); @@ -298,12 +305,15 @@ MarkupView.prototype = { // Fake a childList mutation here. this._mutationObserver([{target: aEvent.target, type: "childList"}]); }.bind(this), true); - } this._containers.set(aNode, container); + // FIXME: set an expando to prevent the the wrapper from disappearing + // See bug 819131 for details. + aNode.__preserveHack = true; container.expanded = aExpand; + container.childrenDirty = true; this._updateChildren(container); if (parent) { @@ -327,6 +337,7 @@ MarkupView.prototype = { if (mutation.type === "attributes" || mutation.type === "characterData") { container.update(); } else if (mutation.type === "childList") { + container.childrenDirty = true; this._updateChildren(container); } } @@ -339,10 +350,12 @@ MarkupView.prototype = { */ showNode: function MT_showNode(aNode, centered) { - this.importNode(aNode); + let container = this.importNode(aNode); + this._updateChildren(container); let walker = documentWalker(aNode); let parent; while (parent = walker.parentNode()) { + this._updateChildren(this.getContainer(parent)); this.expandNode(parent); } LayoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered); @@ -421,9 +434,33 @@ MarkupView.prototype = { this._selectedContainer.selected = true; } + this._ensureSelectionVisible(); + this._selectedContainer.focus(); + return true; }, + /** + * Make sure that every ancestor of the selection are updated + * and included in the list of visible children. + */ + _ensureSelectionVisible: function MT_ensureSelectionVisible() + { + let node = this._selectedContainer.node; + let walker = documentWalker(node); + while (node) { + let container = this._containers.get(node); + let parent = walker.parentNode(); + if (!container.elt.parentNode) { + let parentContainer = this._containers.get(parent); + parentContainer.childrenDirty = true; + this._updateChildren(parentContainer, node); + } + + node = parent; + } + }, + /** * Unmark selected node (no node selected). */ @@ -448,29 +485,139 @@ MarkupView.prototype = { /** * Make sure all children of the given container's node are * imported and attached to the container in the right order. + * @param aCentered If provided, this child will be included + * in the visible subset, and will be roughly centered + * in that list. */ - _updateChildren: function MT__updateChildren(aContainer) + _updateChildren: function MT__updateChildren(aContainer, aCentered) { + if (!aContainer.childrenDirty) { + return false; + } + // Get a tree walker pointing at the first child of the node. let treeWalker = documentWalker(aContainer.node); let child = treeWalker.firstChild(); aContainer.hasChildren = !!child; - if (aContainer.expanded) { - let lastContainer = null; - while (child) { - let container = this.importNode(child, false); - // Make sure children are in the right order. - let before = lastContainer ? lastContainer.nextSibling : aContainer.children.firstChild; - aContainer.children.insertBefore(container.elt, before); - lastContainer = container.elt; - child = treeWalker.nextSibling(); + if (!aContainer.expanded) { + return; + } + + aContainer.childrenDirty = false; + + let children = this._getVisibleChildren(aContainer, aCentered); + let fragment = this.doc.createDocumentFragment(); + + for (child of children.children) { + let container = this.importNode(child, false); + fragment.appendChild(container.elt); + } + + while (aContainer.children.firstChild) { + aContainer.children.removeChild(aContainer.children.firstChild); + } + + if (!(children.hasFirst && children.hasLast)) { + let data = { + showing: this.strings.GetStringFromName("markupView.more.showing"), + showAll: this.strings.formatStringFromName( + "markupView.more.showAll", + [aContainer.node.children.length.toString()], 1), + allButtonClick: function() { + aContainer.maxChildren = -1; + aContainer.childrenDirty = true; + this._updateChildren(aContainer); + }.bind(this) + }; + + if (!children.hasFirst) { + let span = this.template("more-nodes", data); + fragment.insertBefore(span, fragment.firstChild); } - - while (aContainer.children.lastChild != lastContainer) { - aContainer.children.removeChild(aContainer.children.lastChild); + if (!children.hasLast) { + let span = this.template("more-nodes", data); + fragment.appendChild(span); } } + + aContainer.children.appendChild(fragment); + + return true; + }, + + /** + * Return a list of the children to display for this container. + */ + _getVisibleChildren: function MV__getVisibleChildren(aContainer, aCentered) + { + let maxChildren = aContainer.maxChildren || this.maxChildren; + if (maxChildren == -1) { + maxChildren = Number.MAX_VALUE; + } + let firstChild = documentWalker(aContainer.node).firstChild(); + let lastChild = documentWalker(aContainer.node).lastChild(); + + if (!firstChild) { + // No children, we're done. + return { hasFirst: true, hasLast: true, children: [] }; + } + + // By default try to put the selected child in the middle of the list. + let start = aCentered || firstChild; + + // Start by reading backward from the starting point.... + let nodes = []; + let backwardWalker = documentWalker(start); + if (backwardWalker.previousSibling()) { + let backwardCount = Math.floor(maxChildren / 2); + let backwardNodes = this._readBackward(backwardWalker, backwardCount); + nodes = backwardNodes; + } + + // Then read forward by any slack left in the max children... + let forwardWalker = documentWalker(start); + let forwardCount = maxChildren - nodes.length; + nodes = nodes.concat(this._readForward(forwardWalker, forwardCount)); + + // If there's any room left, it means we've run all the way to the end. + // In that case, there might still be more items at the front. + let remaining = maxChildren - nodes.length; + if (remaining > 0 && nodes[0] != firstChild) { + let firstNodes = this._readBackward(backwardWalker, remaining); + + // Then put it all back together. + nodes = firstNodes.concat(nodes); + } + + return { + hasFirst: nodes[0] == firstChild, + hasLast: nodes[nodes.length - 1] == lastChild, + children: nodes + }; + }, + + _readForward: function MV__readForward(aWalker, aCount) + { + let ret = []; + let node = aWalker.currentNode; + do { + ret.push(node); + node = aWalker.nextSibling(); + } while (node && --aCount); + return ret; + }, + + _readBackward: function MV__readBackward(aWalker, aCount) + { + let ret = []; + let node = aWalker.currentNode; + do { + ret.push(node); + node = aWalker.previousSibling(); + } while(node && --aCount); + ret.reverse(); + return ret; }, /** @@ -618,9 +765,7 @@ function MarkupContainer(aMarkupView, aNode) this.expander = null; this.codeBox = null; this.children = null; - let options = { stack: "markup-view.xhtml" }; - this.markup.template("container", this, options); - + this.markup.template("container", this); this.elt.container = this; this.expander.addEventListener("click", function() { @@ -734,7 +879,7 @@ MarkupContainer.prototype = { if (focusable) { focusable.focus(); } - } + }, } /** @@ -841,13 +986,12 @@ function ElementEditor(aContainer, aNode) this.attrList = null; this.newAttr = null; this.closeElt = null; - let options = { stack: "markup-view.xhtml" }; // Create the main editor - this.template("element", this, options); + this.template("element", this); // Create the closing tag - this.template("elementClose", this, options); + this.template("elementClose", this); // Make the tag name editable (unless this is a document element) if (aNode != aNode.ownerDocument.documentElement) { @@ -927,8 +1071,7 @@ ElementEditor.prototype = { let data = { attrName: aAttr.name, }; - let options = { stack: "markup-view.xhtml" }; - this.template("attribute", data, options); + this.template("attribute", data); var {attr, inner, name, val} = data; // Figure out where we should place the attribute. @@ -1260,3 +1403,8 @@ function whitespaceTextFilter(aNode) return Ci.nsIDOMNodeFilter.FILTER_ACCEPT; } } + +XPCOMUtils.defineLazyGetter(MarkupView.prototype, "strings", function () { + return Services.strings.createBundle( + "chrome://browser/locale/devtools/inspector.properties"); +}); diff --git a/browser/devtools/markupview/markup-view.xhtml b/browser/devtools/markupview/markup-view.xhtml index 816ba66a733a..f8db2c3fe597 100644 --- a/browser/devtools/markupview/markup-view.xhtml +++ b/browser/devtools/markupview/markup-view.xhtml @@ -16,6 +16,8 @@