From 1cd45de6ca67a02bbc1629599b242ffb3d365519 Mon Sep 17 00:00:00 2001 From: Edwin Flores Date: Mon, 25 Feb 2013 12:03:55 +1300 Subject: [PATCH 01/66] Bug 829408 - Force stagefright to return readable video frames on Gingerbread on the Galaxy SII r=doublec --- media/omx-plugin/OmxPlugin.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/media/omx-plugin/OmxPlugin.cpp b/media/omx-plugin/OmxPlugin.cpp index 5f90863bbf31..ea75ea336dec 100644 --- a/media/omx-plugin/OmxPlugin.cpp +++ b/media/omx-plugin/OmxPlugin.cpp @@ -22,6 +22,12 @@ #include "android/log.h" +#if !defined(MOZ_ANDROID_FROYO) +#define DEFAULT_STAGEFRIGHT_FLAGS OMXCodec::kClientNeedsFramebuffer +#else +#define DEFAULT_STAGEFRIGHT_FLAGS 0 +#endif + #undef LOG #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "OmxPlugin" , ## args) @@ -285,6 +291,9 @@ static uint32_t GetVideoCreationFlags(PluginHost* aPluginHost) } #endif } + + flags |= DEFAULT_STAGEFRIGHT_FLAGS; + return static_cast(flags); #endif } @@ -294,10 +303,10 @@ static sp CreateVideoSource(PluginHost* aPluginHost, const sp& aVideoTrack) { uint32_t flags = GetVideoCreationFlags(aPluginHost); - if (flags == 0) { + if (flags == DEFAULT_STAGEFRIGHT_FLAGS) { // Let Stagefright choose hardware or software decoder. sp videoSource = OMXCodec::Create(aOmx, aVideoTrack->getFormat(), - false, aVideoTrack, NULL, 0); + false, aVideoTrack, NULL, flags); if (videoSource == NULL) return NULL; @@ -337,13 +346,13 @@ static sp CreateVideoSource(PluginHost* aPluginHost, LOG("Falling back to software decoder"); videoSource.clear(); #if defined(MOZ_ANDROID_V2_X_X) - flags = OMXCodec::kPreferSoftwareCodecs; + flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kPreferSoftwareCodecs; #else - flags = OMXCodec::kSoftwareCodecsOnly; + flags = DEFAULT_STAGEFRIGHT_FLAGS | OMXCodec::kSoftwareCodecsOnly; #endif } - MOZ_ASSERT(flags != 0); + MOZ_ASSERT(flags != DEFAULT_STAGEFRIGHT_FLAGS); return OMXCodec::Create(aOmx, aVideoTrack->getFormat(), false, aVideoTrack, NULL, flags); } From a1bece2060269304c47e24e9e03af65d13ff549d Mon Sep 17 00:00:00 2001 From: "Adam Roach [:abr]" Date: Fri, 22 Feb 2013 14:53:56 -0600 Subject: [PATCH 02/66] Bug 841566 - Turn on testserver logging for WebRTC-related systems r=jesup,ted.mielczarek --- build/automation.py.in | 8 ++++++++ .../third_party/nICEr/src/stun/stun_server_ctx.c | 6 +++++- .../signaling/src/sipcc/core/ccapp/ccapi_snapshot.c | 2 +- media/webrtc/signaling/src/sipcc/core/common/config_api.c | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/build/automation.py.in b/build/automation.py.in index 005b9ce3751b..44d0585ac2bc 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -825,6 +825,14 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1' env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1' + # Additional temporary logging while we try to debug some intermittent + # WebRTC conditions. This is necessary to troubleshoot bugs 841496, + # 841150, and 839677 (at least) + env['NSPR_LOG_MODULES'] = 'signaling:3,mtransport:3' + env['R_LOG_LEVEL'] = '5' + env['R_LOG_DESTINATION'] = 'stderr' + env['R_LOG_VERBOSE'] = '1' + # ASan specific environment stuff if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC): try: 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 0efeef052d60..3ae8a5acfcfe 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 @@ -153,7 +153,11 @@ static int nr_stun_server_get_password(void *arg, nr_stun_message *msg, Data **p ABORT(R_NOT_FOUND); } - r_log(NR_LOG_STUN,LOG_NOTICE,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username); + /* Although this is an exceptional condition, we'll already have seen a + * NOTICE-level log message about the unknown user, so additional log + * messages at any level higher than DEBUG are unnecessary. */ + + r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s): Unable to find password for unknown user: %s",ctx->label,username_attribute->u.username); ABORT(R_NOT_FOUND); } diff --git a/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_snapshot.c b/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_snapshot.c index 11ee155634ad..d177c0970636 100644 --- a/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_snapshot.c +++ b/media/webrtc/signaling/src/sipcc/core/ccapp/ccapi_snapshot.c @@ -30,7 +30,7 @@ cc_string_t lineLabels[MAX_CONFIG_LINES+1] = {0}; void ccsnap_set_line_label(int btn, cc_string_t label) { - CCAPP_ERROR(DEB_F_PREFIX"btn=%d label=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_set_line_label"), btn, label); + CCAPP_DEBUG(DEB_F_PREFIX"btn=%d label=%s\n", DEB_F_PREFIX_ARGS(SIP_CC_PROV, "ccsnap_set_line_label"), btn, label); if ( btn > 0 && btn <= MAX_CONFIG_LINES+1 ) { if ( label == NULL ) { label = strlib_empty(); diff --git a/media/webrtc/signaling/src/sipcc/core/common/config_api.c b/media/webrtc/signaling/src/sipcc/core/common/config_api.c index bd4d87c1cad7..e2b15fd0dbd9 100755 --- a/media/webrtc/signaling/src/sipcc/core/common/config_api.c +++ b/media/webrtc/signaling/src/sipcc/core/common/config_api.c @@ -92,7 +92,7 @@ config_set_string (int id, char *buffer) } } -#define MAX_CONFIG_VAL_PRINT_LEN 256 +#define MAX_CONFIG_VAL_PRINT_LEN 258 /* * Function: print_config_value() * From c75f63ba264c79057b22f812ba4050d96020177a Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Sun, 24 Feb 2013 18:19:10 -0500 Subject: [PATCH 03/66] Bug 843361 - Clean up use of statics when enumerating procs r=blassey --- mobile/android/base/GeckoAppShell.java | 33 ++++++++++++-------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 96df80f1d9a7..908e6718ffd0 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1502,12 +1502,11 @@ public class GeckoAppShell boolean callback(int pid); } - static int sPidColumn = -1; - static int sUserColumn = -1; private static void EnumerateGeckoProcesses(GeckoProcessesVisitor visiter) { + int pidColumn = -1; + int userColumn = -1; try { - // run ps and parse its output java.lang.Process ps = Runtime.getRuntime().exec("ps"); BufferedReader in = new BufferedReader(new InputStreamReader(ps.getInputStream()), @@ -1516,30 +1515,28 @@ public class GeckoAppShell String headerOutput = in.readLine(); // figure out the column offsets. We only care about the pid and user fields - if (sPidColumn == -1 || sUserColumn == -1) { - StringTokenizer st = new StringTokenizer(headerOutput); - - int tokenSoFar = 0; - while(st.hasMoreTokens()) { - String next = st.nextToken(); - if (next.equalsIgnoreCase("PID")) - sPidColumn = tokenSoFar; - else if (next.equalsIgnoreCase("USER")) - sUserColumn = tokenSoFar; - tokenSoFar++; - } + StringTokenizer st = new StringTokenizer(headerOutput); + + int tokenSoFar = 0; + while (st.hasMoreTokens()) { + String next = st.nextToken(); + if (next.equalsIgnoreCase("PID")) + pidColumn = tokenSoFar; + else if (next.equalsIgnoreCase("USER")) + userColumn = tokenSoFar; + tokenSoFar++; } // alright, the rest are process entries. String psOutput = null; while ((psOutput = in.readLine()) != null) { String[] split = psOutput.split("\\s+"); - if (split.length <= sPidColumn || split.length <= sUserColumn) + if (split.length <= pidColumn || split.length <= userColumn) continue; - int uid = android.os.Process.getUidForName(split[sUserColumn]); + int uid = android.os.Process.getUidForName(split[userColumn]); if (uid == android.os.Process.myUid() && !split[split.length - 1].equalsIgnoreCase("ps")) { - int pid = Integer.parseInt(split[sPidColumn]); + int pid = Integer.parseInt(split[pidColumn]); boolean keepGoing = visiter.callback(pid); if (keepGoing == false) break; From 0a0b02780418de1deb4c9e9b5866b06b799f8ca8 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 20 Feb 2013 12:23:47 +1100 Subject: [PATCH 04/66] Bug 833292 - prevent social initialization completing after window is unloaded. r=jaws --- browser/base/content/browser-social.js | 36 ++++++++++++++------------ browser/modules/Social.jsm | 27 +++++++------------ 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/browser/base/content/browser-social.js b/browser/base/content/browser-social.js index 3be48cac1894..31a84384cfb8 100644 --- a/browser/base/content/browser-social.js +++ b/browser/base/content/browser-social.js @@ -31,7 +31,20 @@ let SocialUI = { SocialChatBar.update(); }); - Social.init(this._providerReady.bind(this)); + SocialChatBar.init(); + SocialShareButton.init(); + SocialMenu.init(); + SocialToolbar.init(); + SocialSidebar.init(); + + Social.init(); + // If social was previously initialized it isn't going to notify observers + // about the provider being set or the list of providers changing, so + // handle those now. + if (Social.provider) { + this.observe(null, "social:provider-set", Social.provider.origin); + this.observe(null, "social:providers-changed", null); + } }, // Called on window unload @@ -48,18 +61,6 @@ let SocialUI = { Services.prefs.removeObserver("social.toast-notifications.enabled", this); }, - // Called once, after window load, once Social.jsm's provider has been set. - _providerReady: function SocialUI_providerReady() { - this._updateActiveUI(); - this._updateMenuItems(); - - SocialChatBar.update(); - SocialShareButton.init(); - SocialMenu.populate(); - SocialToolbar.init(); - SocialSidebar.init(); - }, - // Social.provider has changed, update any state that depends on it. // Note: this method is not called when Social.provider is first set, during // the first window load. @@ -335,6 +336,8 @@ let SocialUI = { } let SocialChatBar = { + init: function() { + }, get chatbar() { return document.getElementById("pinnedchats"); }, @@ -556,7 +559,6 @@ let SocialFlyout = { let SocialShareButton = { // Called once, after window load, when the Social.provider object is initialized init: function SSB_init() { - this.updateProvider(); }, // Called when the Social.provider changes @@ -703,6 +705,9 @@ let SocialShareButton = { }; var SocialMenu = { + init: function SocialMenu_init() { + }, + populate: function SocialMenu_populate() { let submenu = document.getElementById("menu_social-statusarea-popup"); let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem"); @@ -740,8 +745,6 @@ var SocialToolbar = { let accesskey = gNavigatorBundle.getString("social.removeProvider.accesskey"); let removeCommand = document.getElementById("Social:Remove"); removeCommand.setAttribute("accesskey", accesskey); - - this.updateProvider(); this._dynamicResizer = new DynamicResizeWatcher(); }, @@ -1073,7 +1076,6 @@ var SocialSidebar = { Social.setErrorListener(sbrowser, this.setSidebarErrorMessage.bind(this)); // setting isAppTab causes clicks on untargeted links to open new tabs sbrowser.docShell.isAppTab = true; - this.update(); }, // Whether the sidebar can be shown for this window. diff --git a/browser/modules/Social.jsm b/browser/modules/Social.jsm index 78212d8d2eb8..ed237f8974b2 100644 --- a/browser/modules/Social.jsm +++ b/browser/modules/Social.jsm @@ -57,13 +57,12 @@ this.Social = { return this._provider; }, set provider(val) { - // Changes triggered by the public setter should notify of an engine change. - this._setProvider(val, true); + this._setProvider(val); }, // Sets the current provider and enables it. Also disables the - // previously set provider, and optionally notifies observers of the change. - _setProvider: function (provider, notify) { + // previously set provider, and notifies observers of the change. + _setProvider: function (provider) { if (this._provider == provider) return; @@ -84,10 +83,8 @@ this.Social = { Services.prefs.setBoolPref("social.enabled", enabled); } - if (notify) { - let origin = this._provider && this._provider.origin; - Services.obs.notifyObservers(null, "social:provider-set", origin); - } + let origin = this._provider && this._provider.origin; + Services.obs.notifyObservers(null, "social:provider-set", origin); }, get defaultProvider() { @@ -97,41 +94,37 @@ this.Social = { return provider || this.providers[0]; }, - init: function Social_init(callback) { + init: function Social_init() { this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled; if (this.providers) { - schedule(callback); return; } // Retrieve the current set of providers, and set the current provider. SocialService.getProviderList(function (providers) { - // We don't want to notify about a provider change when we're setting - // this.provider for the first time, so pass false here. - this._updateProviderCache(providers, false); - callback(); + this._updateProviderCache(providers); }.bind(this)); // Register an observer for changes to the provider list SocialService.registerProviderListener(function providerListener(topic, data) { // An engine change caused by adding/removing a provider should notify if (topic == "provider-added" || topic == "provider-removed") { - this._updateProviderCache(data, true); + this._updateProviderCache(data); Services.obs.notifyObservers(null, "social:providers-changed", null); } }.bind(this)); }, // Called to update our cache of providers and set the current provider - _updateProviderCache: function (providers, notifyProviderChange) { + _updateProviderCache: function (providers) { this.providers = providers; // If social is currently disabled there's nothing else to do. if (!SocialService.enabled) return; // Otherwise set the provider. - this._setProvider(this.defaultProvider, notifyProviderChange); + this._setProvider(this.defaultProvider); }, set enabled(val) { From 4f7ce904597814f8400f07a5e18a44b123eff8e9 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:20 -0600 Subject: [PATCH 05/66] Bug 689623. Part 1. The display list we build for image visibility means we hit the same assertions as we hit when building the painting display list. So adjust some reftest assert counts upwards. r=mats --- layout/generic/crashtests/crashtests.list | 6 +++--- layout/reftests/bugs/reftest.list | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 85bc47a92e35..9abe1bc4be59 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -24,7 +24,7 @@ load 321224.xul load 322780-1.xul load 323381-1.html load 323381-2.html -asserts-if(gtk2Widget,13-14) load 323386-1.html # Bug 575011 +asserts-if(gtk2Widget,13-15) load 323386-1.html # Bug 575011 load 323389-1.html load 323389-2.html load 323493-1.html @@ -296,8 +296,8 @@ load 465651-1.html load 467137-1.html load 467213-1.html load 467487-1.html -asserts(6-9) load 467493-1.html -asserts(4-8) load 467493-2.html +asserts(6-10) load 467493-1.html +asserts(4-9) load 467493-2.html load 467875-1.xhtml load 467914-1.html load 468207-1.html diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 13f526af99af..2de5dc853c3c 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1568,7 +1568,7 @@ skip-if(B2G) fails-if(Android) random-if(layersGPUAccelerated) fails-if(/^Window == 582037-1b.html 582037-1-ref.html skip-if(B2G) == 582037-2a.html 582037-2-ref.html skip-if(B2G) == 582037-2b.html 582037-2-ref.html -asserts(0-12) == 582146-1.html about:blank +asserts(0-13) == 582146-1.html about:blank skip-if(B2G) == 582476-1.svg 582476-1-ref.svg == 584400-dash-length.svg 584400-dash-length-ref.svg == 584699-1.html 584699-1-ref.html From 7796029ae8af5377c0c7e415ef5d2e926dca1ab9 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:21 -0600 Subject: [PATCH 06/66] Bug 689623. Part 2. In nsImageLoadingContent make the discard request an optional part of UntrackImage so that we can use UntrackImage everywhere instead of RemoveImage. r=joe --- content/base/src/nsImageLoadingContent.cpp | 38 +++++++--------------- content/base/src/nsImageLoadingContent.h | 8 ++++- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index f09daed1c8c6..9faf20f20116 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -423,20 +423,8 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame) &mPendingRequestRegistered); } - if (mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) { - nsIDocument* doc = GetOurCurrentDoc(); - if (doc) { - mCurrentRequestFlags &= ~REQUEST_IS_TRACKED; - doc->RemoveImage(mCurrentRequest); - } - } - if (mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) { - nsIDocument* doc = GetOurCurrentDoc(); - if (doc) { - mPendingRequestFlags &= ~REQUEST_IS_TRACKED; - doc->RemoveImage(mPendingRequest); - } - } + UntrackImage(mCurrentRequest); + UntrackImage(mPendingRequest); } int32_t @@ -1101,7 +1089,7 @@ nsImageLoadingContent::ClearCurrentRequest(nsresult aReason) &mCurrentRequestRegistered); // Clean up the request. - UntrackImage(mCurrentRequest); + UntrackImage(mCurrentRequest, REQUEST_DISCARD); mCurrentRequest->CancelAndForgetObserver(aReason); mCurrentRequest = nullptr; mCurrentRequestFlags = 0; @@ -1124,7 +1112,7 @@ nsImageLoadingContent::ClearPendingRequest(nsresult aReason) nsLayoutUtils::DeregisterImageRequest(GetFramePresContext(), mPendingRequest, &mPendingRequestRegistered); - UntrackImage(mPendingRequest); + UntrackImage(mPendingRequest, REQUEST_DISCARD); mPendingRequest->CancelAndForgetObserver(aReason); mPendingRequest = nullptr; mPendingRequestFlags = 0; @@ -1211,14 +1199,8 @@ nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) nsCxPusher pusher; pusher.PushNull(); - if (mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) { - mCurrentRequestFlags &= ~REQUEST_IS_TRACKED; - doc->RemoveImage(mCurrentRequest); - } - if (mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) { - mPendingRequestFlags &= ~REQUEST_IS_TRACKED; - doc->RemoveImage(mPendingRequest); - } + UntrackImage(mCurrentRequest); + UntrackImage(mPendingRequest); if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD) doc->UnblockOnload(false); @@ -1248,7 +1230,7 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage) } nsresult -nsImageLoadingContent::UntrackImage(imgIRequest* aImage) +nsImageLoadingContent::UntrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */) { if (!aImage) return NS_OK; @@ -1263,11 +1245,13 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage) if (doc) { if (aImage == mCurrentRequest && (mCurrentRequestFlags & REQUEST_IS_TRACKED)) { mCurrentRequestFlags &= ~REQUEST_IS_TRACKED; - doc->RemoveImage(mCurrentRequest, nsIDocument::REQUEST_DISCARD); + doc->RemoveImage(mCurrentRequest, + (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0); } if (aImage == mPendingRequest && (mPendingRequestFlags & REQUEST_IS_TRACKED)) { mPendingRequestFlags &= ~REQUEST_IS_TRACKED; - doc->RemoveImage(mPendingRequest, nsIDocument::REQUEST_DISCARD); + doc->RemoveImage(mPendingRequest, + (aFlags & REQUEST_DISCARD) ? nsIDocument::REQUEST_DISCARD : 0); } } return NS_OK; diff --git a/content/base/src/nsImageLoadingContent.h b/content/base/src/nsImageLoadingContent.h index 8c5cec598f6d..308e3c5bc044 100644 --- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -326,9 +326,15 @@ protected: * Adds/Removes a given imgIRequest from our document's tracker. * * No-op if aImage is null. + * + * REQUEST_DISCARD passed to UntrackImage means we request the discard of the + * decoded data of the image. */ nsresult TrackImage(imgIRequest* aImage); - nsresult UntrackImage(imgIRequest* aImage); + enum { + REQUEST_DISCARD = 0x1 + }; + nsresult UntrackImage(imgIRequest* aImage, uint32_t aFlags = 0); /* MEMBERS */ nsRefPtr mCurrentRequest; From 2bf3ac076623fd66b4d305f03698f484fe4e8989 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:21 -0600 Subject: [PATCH 07/66] Bug 689623. Part 3. In nsImageLoadingContent add a flag to TrackImage that skips the check if we have a frame so that we can use TrackImage everywhere (including FrameCreated) instead of AddImage. r=joe --- content/base/src/nsImageLoadingContent.cpp | 36 ++++++---------------- content/base/src/nsImageLoadingContent.h | 8 ++++- 2 files changed, 16 insertions(+), 28 deletions(-) diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index 9faf20f20116..77c73710cfc3 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -375,25 +375,15 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame) { NS_ASSERTION(aFrame, "aFrame is null"); + // We pass the SKIP_FRAME_CHECK flag to TrackImage here because our primary + // frame pointer hasn't been setup yet when this is caled. + TrackImage(mCurrentRequest, SKIP_FRAME_CHECK); + TrackImage(mPendingRequest, SKIP_FRAME_CHECK); + // We need to make sure that our image request is registered, if it should // be registered. nsPresContext* presContext = aFrame->PresContext(); - if (mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) { - nsIDocument* doc = GetOurCurrentDoc(); - if (doc) { - mCurrentRequestFlags |= REQUEST_IS_TRACKED; - doc->AddImage(mCurrentRequest); - } - } - if (mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) { - nsIDocument* doc = GetOurCurrentDoc(); - if (doc) { - mPendingRequestFlags |= REQUEST_IS_TRACKED; - doc->AddImage(mPendingRequest); - } - } - if (mCurrentRequest) { nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest, &mCurrentRequestRegistered); @@ -1171,16 +1161,8 @@ nsImageLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsCxPusher pusher; pusher.PushNull(); - if (GetOurPrimaryFrame()) { - if (mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) { - mCurrentRequestFlags |= REQUEST_IS_TRACKED; - aDocument->AddImage(mCurrentRequest); - } - if (mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) { - mPendingRequestFlags |= REQUEST_IS_TRACKED; - aDocument->AddImage(mPendingRequest); - } - } + TrackImage(mCurrentRequest); + TrackImage(mPendingRequest); if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD) aDocument->BlockOnload(); @@ -1207,7 +1189,7 @@ nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent) } nsresult -nsImageLoadingContent::TrackImage(imgIRequest* aImage) +nsImageLoadingContent::TrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */) { if (!aImage) return NS_OK; @@ -1216,7 +1198,7 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage) "Why haven't we heard of this request?"); nsIDocument* doc = GetOurCurrentDoc(); - if (doc && GetOurPrimaryFrame()) { + if (doc && ((aFlags & SKIP_FRAME_CHECK) || GetOurPrimaryFrame())) { if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) { mCurrentRequestFlags |= REQUEST_IS_TRACKED; doc->AddImage(mCurrentRequest); diff --git a/content/base/src/nsImageLoadingContent.h b/content/base/src/nsImageLoadingContent.h index 308e3c5bc044..6c86cfd18d30 100644 --- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -327,10 +327,16 @@ protected: * * No-op if aImage is null. * + * SKIP_FRAME_CHECK passed to TrackImage means we skip the check if we have a + * frame, there is only one valid use of this: when calling from FrameCreated. + * * REQUEST_DISCARD passed to UntrackImage means we request the discard of the * decoded data of the image. */ - nsresult TrackImage(imgIRequest* aImage); + enum { + SKIP_FRAME_CHECK = 0x1 + }; + nsresult TrackImage(imgIRequest* aImage, uint32_t aFlags = 0); enum { REQUEST_DISCARD = 0x1 }; From 8ca393d546d1b0be4561f0902a4b8d657ae87cfa Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:21 -0600 Subject: [PATCH 08/66] Bug 689623. Part 4. Add a visible count to nsIImadeLoadingContent, and use it when locking the image. r=joe --- .../base/public/nsIImageLoadingContent.idl | 10 +++++- content/base/src/nsImageLoadingContent.cpp | 34 +++++++++++++++++-- content/base/src/nsImageLoadingContent.h | 2 ++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/content/base/public/nsIImageLoadingContent.idl b/content/base/public/nsIImageLoadingContent.idl index 8734a61c8302..88021e6752df 100644 --- a/content/base/public/nsIImageLoadingContent.idl +++ b/content/base/public/nsIImageLoadingContent.idl @@ -37,7 +37,7 @@ interface nsIFrame; * Web IDL interfaces to mirror this interface when changing it. */ -[scriptable, builtinclass, uuid(497bfb9b-d996-4d1e-a647-8137b0cfc876)] +[scriptable, builtinclass, uuid(e3968acd-b796-4ca3-aec0-e7f0880f2219)] interface nsIImageLoadingContent : imgINotificationObserver { /** @@ -159,4 +159,12 @@ interface nsIImageLoadingContent : imgINotificationObserver * as PR_FALSE to revert ImageState() to its original behaviour. */ void forceImageState(in boolean aForce, in unsigned long long aState); + + /** + * A visible count is stored, if it is non-zero then this image is considered + * visible. These methods increment, decrement, or return the visible coount. + */ + [noscript, notxpcom] void IncrementVisibleCount(); + [noscript, notxpcom] void DecrementVisibleCount(); + [noscript, notxpcom] uint32_t GetVisibleCount(); }; diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index 77c73710cfc3..df52f3c577d2 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -86,7 +86,8 @@ nsImageLoadingContent::nsImageLoadingContent() mNewRequestsWillNeedAnimationReset(false), mStateChangerDepth(0), mCurrentRequestRegistered(false), - mPendingRequestRegistered(false) + mPendingRequestRegistered(false), + mVisibleCount(0) { if (!nsContentUtils::GetImgLoaderForChannel(nullptr)) { mLoadingEnabled = false; @@ -592,6 +593,34 @@ nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest) return NS_OK; } +void +nsImageLoadingContent::IncrementVisibleCount() +{ + mVisibleCount++; + if (mVisibleCount == 1) { + TrackImage(mCurrentRequest); + TrackImage(mPendingRequest); + } +} + +void +nsImageLoadingContent::DecrementVisibleCount() +{ + NS_ASSERTION(mVisibleCount > 0, "visible count should be positive here"); + mVisibleCount--; + + if (mVisibleCount == 0) { + UntrackImage(mCurrentRequest); + UntrackImage(mPendingRequest); + } +} + +uint32_t +nsImageLoadingContent::GetVisibleCount() +{ + return mVisibleCount; +} + /* * Non-interface methods */ @@ -1198,7 +1227,8 @@ nsImageLoadingContent::TrackImage(imgIRequest* aImage, uint32_t aFlags /* = 0 */ "Why haven't we heard of this request?"); nsIDocument* doc = GetOurCurrentDoc(); - if (doc && ((aFlags & SKIP_FRAME_CHECK) || GetOurPrimaryFrame())) { + if (doc && ((aFlags & SKIP_FRAME_CHECK) || GetOurPrimaryFrame()) && + (mVisibleCount > 0)) { if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) { mCurrentRequestFlags |= REQUEST_IS_TRACKED; doc->AddImage(mCurrentRequest); diff --git a/content/base/src/nsImageLoadingContent.h b/content/base/src/nsImageLoadingContent.h index 6c86cfd18d30..26cbc64de442 100644 --- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -416,6 +416,8 @@ private: // registered with the refresh driver. bool mCurrentRequestRegistered; bool mPendingRequestRegistered; + + uint32_t mVisibleCount; }; #endif // nsImageLoadingContent_h__ From 76533032be9ad238840b284c902c40405a3099b1 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:21 -0600 Subject: [PATCH 09/66] Bug 689623. Part 5. Add mechanism to update image visibility upon some amount of scrolling. r=mats --- layout/base/nsDisplayList.h | 6 +++ layout/generic/nsGfxScrollFrame.cpp | 63 +++++++++++++++++++++++++++++ layout/generic/nsGfxScrollFrame.h | 3 ++ 3 files changed, 72 insertions(+) diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 4201e4ff1bdb..6caabf4bb9db 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -125,6 +125,7 @@ public: PAINTING, EVENT_DELIVERY, PLUGIN_GEOMETRY, + IMAGE_VISIBILITY, OTHER }; nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, bool aBuildCaret); @@ -158,6 +159,11 @@ public: * @return true if the display list is being built for painting. */ bool IsForPainting() { return mMode == PAINTING; } + /** + * @return true if the display list is being built for determining image + * visibility. + */ + bool IsForImageVisibility() { return mMode == IMAGE_VISIBILITY; } bool WillComputePluginGeometry() { return mWillComputePluginGeometry; } /** * @return true if "painting is suppressed" during page load and we diff --git a/layout/generic/nsGfxScrollFrame.cpp b/layout/generic/nsGfxScrollFrame.cpp index e8c54123a508..5d1d8be2e5c3 100644 --- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -54,6 +54,8 @@ #include "nsRefreshDriver.h" #include "nsContentList.h" #include +#include // for std::abs(int/long) +#include // for std::abs(float/double) using namespace mozilla; using namespace mozilla::dom; @@ -1427,6 +1429,7 @@ nsGfxScrollFrameInner::nsGfxScrollFrameInner(nsContainerFrame* aOuter, , mRestorePos(-1, -1) , mLastPos(-1, -1) , mScrollPosForLayerPixelAlignment(-1, -1) + , mLastUpdateImagesPos(-1, -1) , mNeverHasVerticalScrollbar(false) , mNeverHasHorizontalScrollbar(false) , mHasVerticalScrollbar(false) @@ -1880,6 +1883,35 @@ nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt, const nsRect& aRange) return; } + bool needImageVisibilityUpdate = (mLastUpdateImagesPos == nsPoint(-1,-1)); + + static bool sImageVisPrefsCached = false; + // The fraction of the scrollport we allow to scroll by before we schedule + // an update of image visibility. + static int32_t sHorzScrollFraction = 2; + static int32_t sVertScrollFraction = 2; + if (!sImageVisPrefsCached) { + Preferences::AddIntVarCache(&sHorzScrollFraction, + "layout.imagevisibility.amountscrollbeforeupdatehorizontal", 2); + Preferences::AddIntVarCache(&sVertScrollFraction, + "layout.imagevisibility.amountscrollbeforeupdatevertical", 2); + sImageVisPrefsCached = true; + } + + nsPoint dist(std::abs(pt.x - mLastUpdateImagesPos.x), + std::abs(pt.y - mLastUpdateImagesPos.y)); + nscoord horzAllowance = std::max(mScrollPort.width / std::max(sHorzScrollFraction, 1), + nsPresContext::AppUnitsPerCSSPixel()); + nscoord vertAllowance = std::max(mScrollPort.height / std::max(sVertScrollFraction, 1), + nsPresContext::AppUnitsPerCSSPixel()); + if (dist.x >= horzAllowance || dist.y >= vertAllowance) { + needImageVisibilityUpdate = true; + } + + if (needImageVisibilityUpdate) { + presContext->PresShell()->ScheduleImageVisibilityUpdate(); + } + // notify the listeners. for (uint32_t i = 0; i < mListeners.Length(); i++) { mListeners[i]->ScrollPositionWillChange(pt.x, pt.y); @@ -1995,6 +2027,10 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { + if (aBuilder->IsForImageVisibility()) { + mLastUpdateImagesPos = GetScrollPosition(); + } + mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists); if (aBuilder->IsPaintingToWindow()) { @@ -2061,6 +2097,33 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder, dirtyRect = displayPort; } + if (aBuilder->IsForImageVisibility()) { + static bool sImageVisPrefsCached = false; + // The number of scrollports wide/high to expand when looking for images. + static uint32_t sHorzExpandScrollPort = 0; + static uint32_t sVertExpandScrollPort = 1; + if (!sImageVisPrefsCached) { + Preferences::AddUintVarCache(&sHorzExpandScrollPort, + "layout.imagevisibility.numscrollportwidths", (uint32_t)0); + Preferences::AddUintVarCache(&sVertExpandScrollPort, + "layout.imagevisibility.numscrollportheights", 1); + sImageVisPrefsCached = true; + } + + // We expand the dirty rect to catch images just outside of the scroll port. + // We could be smarter and not expand the dirty rect in a direction in which + // we are not able to scroll. + nsRect dirtyRectBefore = dirtyRect; + + nsPoint vertShift = nsPoint(0, sVertExpandScrollPort * dirtyRectBefore.height); + dirtyRect = dirtyRect.Union(dirtyRectBefore - vertShift); + dirtyRect = dirtyRect.Union(dirtyRectBefore + vertShift); + + nsPoint horzShift = nsPoint(sHorzExpandScrollPort * dirtyRectBefore.width, 0); + dirtyRect = dirtyRect.Union(dirtyRectBefore - horzShift); + dirtyRect = dirtyRect.Union(dirtyRectBefore + horzShift); + } + nsDisplayListCollection set; mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, set); diff --git a/layout/generic/nsGfxScrollFrame.h b/layout/generic/nsGfxScrollFrame.h index 025296cfca15..224387115bc0 100644 --- a/layout/generic/nsGfxScrollFrame.h +++ b/layout/generic/nsGfxScrollFrame.h @@ -299,6 +299,9 @@ public: nsCOMPtr mScrollActivityTimer; nsPoint mScrollPosForLayerPixelAlignment; + // The scroll position where we last updated image visibility. + nsPoint mLastUpdateImagesPos; + bool mNeverHasVerticalScrollbar:1; bool mNeverHasHorizontalScrollbar:1; bool mHasVerticalScrollbar:1; From 8729dcd152a902ffc85da3ad22f2e23efeaaa9d9 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:22 -0600 Subject: [PATCH 10/66] Bug 689623. Part 6. Keep a list of visible images on the presshell and code to manage it. r=mats --- layout/base/nsIPresShell.h | 13 ++- layout/base/nsPresShell.cpp | 121 +++++++++++++++++++++++++- layout/base/nsPresShell.h | 14 +++ layout/generic/nsSubDocumentFrame.cpp | 11 ++- 4 files changed, 152 insertions(+), 7 deletions(-) diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index 4e45592adec9..a93cca2124cb 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -120,10 +120,10 @@ typedef struct CapturingContentInfo { nsIContent* mContent; } CapturingContentInfo; -// da75e297-2c23-43c4-8d7f-96a668e96ba9 +// 15f6c268-e40f-42a9-a7eb-e5e10a5840a1 #define NS_IPRESSHELL_IID \ -{ 0xda75e297, 0x2c23, 0x43c4, \ - { 0x8d, 0x7f, 0x96, 0xa6, 0x68, 0xe9, 0x6b, 0xa9 } } +{ 0x15f6c268, 0xe40f, 0x42a9, \ + { 0xa7, 0xeb, 0xe5, 0xe1, 0x0a, 0x58, 0x40, 0xa1 } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -1320,6 +1320,13 @@ public: void InvalidatePresShellIfHidden(); + // Schedule an update of the list of visible images. + virtual void ScheduleImageVisibilityUpdate() = 0; + + // Clears the current list of visible images on this presshell and replaces it + // with images that are in the display list aList. + virtual void RebuildImageVisibility(const nsDisplayList& aList) = 0; + /** * Refresh observer management. */ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ff923df68f27..fc1daefefd12 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -182,6 +182,7 @@ #include "nsTransitionManager.h" #include "LayerTreeInvalidation.h" #include "nsAsyncDOMEvent.h" +#include "nsIImageLoadingContent.h" #define ANCHOR_SCROLL_FLAGS \ (nsIPresShell::SCROLL_OVERFLOW_HIDDEN | nsIPresShell::SCROLL_NO_PARENT_FRAMES) @@ -1001,6 +1002,10 @@ PresShell::Destroy() mSynthMouseMoveEvent.Revoke(); + mUpdateImageVisibilityEvent.Revoke(); + + ClearVisibleImagesList(); + if (mCaret) { mCaret->Terminate(); mCaret = nullptr; @@ -3613,8 +3618,10 @@ PresShell::UnsuppressAndInvalidate() if (win) win->SetReadyForFocus(); - if (!mHaveShutDown) + if (!mHaveShutDown) { SynthesizeMouseMove(false); + ScheduleImageVisibilityUpdate(); + } } void @@ -5278,6 +5285,116 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll) } } +/* static */ void +PresShell::MarkImagesInListVisible(const nsDisplayList& aList) +{ + for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { + nsDisplayList* sublist = item->GetChildren(); + if (sublist) { + MarkImagesInListVisible(*sublist); + continue; + } + nsIFrame* f = item->GetUnderlyingFrame(); + // We could check the type of the display item, only a handful can hold an + // image loading content. + if (f) { + // dont bother nscomptr here, it is wasteful + nsCOMPtr content(do_QueryInterface(f->GetContent())); + if (content) { + // use the presshell containing the image + PresShell* presShell = static_cast(f->PresContext()->PresShell()); + if (presShell) { + content->IncrementVisibleCount(); + presShell->mVisibleImages.AppendElement(content); + } + } + } + } +} + +void +PresShell::RebuildImageVisibility(const nsDisplayList& aList) +{ + nsTArray< nsCOMPtr > beforeimagelist; + beforeimagelist.SwapElements(mVisibleImages); + MarkImagesInListVisible(aList); + for (uint32_t i = 0; i < beforeimagelist.Length(); ++i) { + beforeimagelist[i]->DecrementVisibleCount(); + } +} + +void +PresShell::ClearVisibleImagesList() +{ + for (uint32_t i = 0; i < mVisibleImages.Length(); ++i) { + mVisibleImages[i]->DecrementVisibleCount(); + } + mVisibleImages.Clear(); +} + +void +PresShell::UpdateImageVisibility() +{ + MOZ_ASSERT(!mPresContext || mPresContext->IsRootContentDocument(), + "updating image visibility on a non-root content document?"); + + mUpdateImageVisibilityEvent.Revoke(); + + if (mHaveShutDown || mIsDestroying) { + return; + } + + // call update on that frame + nsIFrame* rootFrame = GetRootFrame(); + if (!rootFrame) { + ClearVisibleImagesList(); + return; + } + + // We could walk the frame tree directly and skip creating a display list for + // better perf. + nsRect updateRect(nsPoint(0, 0), rootFrame->GetSize()); + nsDisplayListBuilder builder(rootFrame, nsDisplayListBuilder::IMAGE_VISIBILITY, true); + builder.IgnorePaintSuppression(); + builder.EnterPresShell(rootFrame, updateRect); + nsDisplayList list; + rootFrame->BuildDisplayListForStackingContext(&builder, updateRect, &list); + builder.LeavePresShell(rootFrame, updateRect); + + RebuildImageVisibility(list); + + list.DeleteAll(); +} + +void +PresShell::ScheduleImageVisibilityUpdate() +{ + if (!mPresContext) + return; + + if (!mPresContext->IsRootContentDocument()) { + nsPresContext* presContext = mPresContext->GetToplevelContentDocumentPresContext(); + if (!presContext) + return; + MOZ_ASSERT(presContext->IsRootContentDocument(), + "Didn't get a root prescontext from GetToplevelContentDocumentPresContext?"); + presContext->PresShell()->ScheduleImageVisibilityUpdate(); + return; + } + + if (mHaveShutDown || mIsDestroying) + return; + + if (mUpdateImageVisibilityEvent.IsPending()) + return; + + nsRefPtr > ev = + NS_NewRunnableMethod(this, &PresShell::UpdateImageVisibility); + if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { + mUpdateImageVisibilityEvent = ev; + } +} + class nsAutoNotifyDidPaint { public: @@ -7281,6 +7398,8 @@ FreezeSubDocument(nsIDocument *aDocument, void *aData) void PresShell::Freeze() { + mUpdateImageVisibilityEvent.Revoke(); + MaybeReleaseCapturingContent(); mDocument->EnumerateFreezableElements(FreezeElement, nullptr); diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index eca334ffd761..ac128d8db3fd 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -335,6 +335,10 @@ public: uint32_t mContentToScrollToFlags; }; + virtual void ScheduleImageVisibilityUpdate(); + + virtual void RebuildImageVisibility(const nsDisplayList& aList); + protected: virtual ~PresShell(); @@ -703,6 +707,16 @@ protected: virtual void ThemeChanged() { mPresContext->ThemeChanged(); } virtual void BackingScaleFactorChanged() { mPresContext->UIResolutionChanged(); } + void UpdateImageVisibility(); + + nsRevocableEventPtr > mUpdateImageVisibilityEvent; + + void ClearVisibleImagesList(); + static void MarkImagesInListVisible(const nsDisplayList& aList); + + // A list of images that are visible or almost visible. + nsTArray< nsCOMPtr > mVisibleImages; + #ifdef DEBUG // The reflow root under which we're currently reflowing. Null when // not in reflow. diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index e91854b5cd98..e98602cafdc2 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -465,10 +465,15 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, childItems.AppendToTop(item); } - if (mIsInline) { - WrapReplacedContentForBorderRadius(aBuilder, &childItems, aLists); + if (aBuilder->IsForImageVisibility()) { + // We don't add the childItems to the return list as we're dealing with them here. + presShell->RebuildImageVisibility(childItems); } else { - aLists.Content()->AppendToTop(&childItems); + if (mIsInline) { + WrapReplacedContentForBorderRadius(aBuilder, &childItems, aLists); + } else { + aLists.Content()->AppendToTop(&childItems); + } } // delete childItems in case of OOM From 4c9c378b53cc7c30b8df53f92322a109a2183a93 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:22 -0600 Subject: [PATCH 11/66] Bug 689623. Part 7. Clear the list of visible images on presshells that we don't descend into while building the display list of visible images. r=mats --- layout/base/nsPresShell.cpp | 22 ++++++++++++++++++++++ layout/base/nsPresShell.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index fc1daefefd12..61d50f8bbaf5 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5315,6 +5315,8 @@ PresShell::MarkImagesInListVisible(const nsDisplayList& aList) void PresShell::RebuildImageVisibility(const nsDisplayList& aList) { + MOZ_ASSERT(!mImageVisibilityVisited, "already visited?"); + mImageVisibilityVisited = true; nsTArray< nsCOMPtr > beforeimagelist; beforeimagelist.SwapElements(mVisibleImages); MarkImagesInListVisible(aList); @@ -5323,6 +5325,22 @@ PresShell::RebuildImageVisibility(const nsDisplayList& aList) } } +/* static */ void +PresShell::ClearImageVisibilityVisited(nsView* aView, bool aClear) +{ + nsViewManager* vm = aView->GetViewManager(); + if (aClear) { + PresShell* presShell = static_cast(vm->GetPresShell()); + if (!presShell->mImageVisibilityVisited) { + presShell->ClearVisibleImagesList(); + } + presShell->mImageVisibilityVisited = false; + } + for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { + ClearImageVisibilityVisited(v, v->GetViewManager() != vm); + } +} + void PresShell::ClearVisibleImagesList() { @@ -5363,6 +5381,8 @@ PresShell::UpdateImageVisibility() RebuildImageVisibility(list); + ClearImageVisibilityVisited(rootFrame->GetView(), true); + list.DeleteAll(); } @@ -5421,6 +5441,8 @@ PresShell::Paint(nsView* aViewToPaint, NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell"); NS_ASSERTION(aViewToPaint, "null view"); + MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared"); + if (!mIsActive || mIsZombie) { return; } diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index ac128d8db3fd..290473f324a7 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -712,6 +712,7 @@ protected: nsRevocableEventPtr > mUpdateImageVisibilityEvent; void ClearVisibleImagesList(); + static void ClearImageVisibilityVisited(nsView* aView, bool aClear); static void MarkImagesInListVisible(const nsDisplayList& aList); // A list of images that are visible or almost visible. @@ -806,6 +807,8 @@ protected: bool mAsyncResizeTimerIsActive : 1; bool mInResize : 1; + bool mImageVisibilityVisited : 1; + static bool sDisableNonTestMouseEvents; }; From 45f8b4f1f94e3b6a88055c3094ce23180fcbb025 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:22 -0600 Subject: [PATCH 12/66] Bug 689623. Part 8. Add an 'unlocked draw' notification for images that are drawn when not locked so we catch any images that become visible through a means other than scrolling. r=joe,mats --- content/base/src/nsImageLoadingContent.cpp | 19 ++++++++++++++++ content/base/src/nsImageLoadingContent.h | 1 + image/public/imgINotificationObserver.idl | 5 +++-- image/src/RasterImage.cpp | 9 ++++++++ image/src/imgDecoderObserver.h | 5 +++++ image/src/imgRequestProxy.cpp | 11 +++++++++ image/src/imgRequestProxy.h | 1 + image/src/imgStatusTracker.cpp | 26 ++++++++++++++++++++++ image/src/imgStatusTracker.h | 2 ++ layout/base/nsIPresShell.h | 10 ++++++--- layout/base/nsPresShell.cpp | 21 +++++++++++++++++ layout/base/nsPresShell.h | 2 ++ 12 files changed, 107 insertions(+), 5 deletions(-) diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index df52f3c577d2..dcc3d7fcc144 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -122,6 +122,11 @@ nsImageLoadingContent::Notify(imgIRequest* aRequest, return OnImageIsAnimated(aRequest); } + if (aType == imgINotificationObserver::UNLOCKED_DRAW) { + OnUnlockedDraw(); + return NS_OK; + } + if (aType == imgINotificationObserver::LOAD_COMPLETE) { // We should definitely have a request here NS_ABORT_IF_FALSE(aRequest, "no request?"); @@ -229,6 +234,20 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, return NS_OK; } +void +nsImageLoadingContent::OnUnlockedDraw() +{ + nsPresContext* presContext = GetFramePresContext(); + if (!presContext) + return; + + nsIPresShell* presShell = presContext->PresShell(); + if (!presShell) + return; + + presShell->EnsureImageInVisibleList(this); +} + nsresult nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest) { diff --git a/content/base/src/nsImageLoadingContent.h b/content/base/src/nsImageLoadingContent.h index 26cbc64de442..eb363cfeff9c 100644 --- a/content/base/src/nsImageLoadingContent.h +++ b/content/base/src/nsImageLoadingContent.h @@ -183,6 +183,7 @@ protected: void UnbindFromTree(bool aDeep, bool aNullParent); nsresult OnStopRequest(imgIRequest* aRequest, nsresult aStatus); + void OnUnlockedDraw(); nsresult OnImageIsAnimated(imgIRequest *aRequest); private: diff --git a/image/public/imgINotificationObserver.idl b/image/public/imgINotificationObserver.idl index 8b9c56bb1889..98a5a3ad3d37 100644 --- a/image/public/imgINotificationObserver.idl +++ b/image/public/imgINotificationObserver.idl @@ -14,7 +14,7 @@ interface imgIRequest; [ptr] native nsIntRect(nsIntRect); -[scriptable, builtinclass, uuid(90b3d21c-317d-4d96-93c0-12add64a26bf)] +[scriptable, builtinclass, uuid(ac65c702-7771-4f6d-b18b-1c7d806ce3c1)] interface imgINotificationObserver : nsISupports { const long SIZE_AVAILABLE = 1; @@ -23,7 +23,8 @@ interface imgINotificationObserver : nsISupports const long LOAD_COMPLETE = 4; const long DECODE_COMPLETE = 5; const long DISCARD = 6; - const long IS_ANIMATED = 7; + const long UNLOCKED_DRAW = 7; + const long IS_ANIMATED = 8; [noscript] void notify(in imgIRequest aProxy, in long aType, [const] in nsIntRect aRect); }; diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 4453388640ed..51f8d1b77229 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -3107,6 +3107,15 @@ RasterImage::Draw(gfxContext *aContext, DiscardTracker::Reset(&mDiscardTrackerNode); } + // We would like to just check if we have a zero lock count, but we can't do + // that for animated images because in EnsureAnimExists we lock the image and + // never unlock so that animated images always have their lock count >= 1. In + // that case we use our animation consumers count as a proxy for lock count. + if (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)) { + if (mStatusTracker) + mStatusTracker->GetDecoderObserver()->OnUnlockedDraw(); + } + // We use !mDecoded && mHasSourceData to mean discarded. if (!mDecoded && mHasSourceData) { mDrawStartTime = TimeStamp::Now(); diff --git a/image/src/imgDecoderObserver.h b/image/src/imgDecoderObserver.h index 57b6a01b66fc..f34d24fb1303 100644 --- a/image/src/imgDecoderObserver.h +++ b/image/src/imgDecoderObserver.h @@ -109,6 +109,11 @@ public: * image will initiate a new series of progressive decode notifications. */ virtual void OnDiscard() = 0; + + /** + * Called when we are asked to Draw an image that is not locked. + */ + virtual void OnUnlockedDraw() = 0; }; // We must define a destructor because derived classes call our destructor from diff --git a/image/src/imgRequestProxy.cpp b/image/src/imgRequestProxy.cpp index 737d7b6bec49..c815bd4f9303 100644 --- a/image/src/imgRequestProxy.cpp +++ b/image/src/imgRequestProxy.cpp @@ -767,6 +767,17 @@ void imgRequestProxy::OnDiscard() } } +void imgRequestProxy::OnUnlockedDraw() +{ + LOG_FUNC(GetImgLog(), "imgRequestProxy::OnUnlockedDraw"); + + if (mListener && !mCanceled) { + // Hold a ref to the listener while we call it, just in case. + nsCOMPtr kungFuDeathGrip(mListener); + mListener->Notify(this, imgINotificationObserver::UNLOCKED_DRAW, nullptr); + } +} + void imgRequestProxy::OnImageIsAnimated() { LOG_FUNC(GetImgLog(), "imgRequestProxy::OnImageIsAnimated"); diff --git a/image/src/imgRequestProxy.h b/image/src/imgRequestProxy.h index ff48e91d81dc..68332b7101a2 100644 --- a/image/src/imgRequestProxy.h +++ b/image/src/imgRequestProxy.h @@ -143,6 +143,7 @@ protected: void OnStopFrame (); void OnStopDecode (); void OnDiscard (); + void OnUnlockedDraw (); void OnImageIsAnimated (); /* non-virtual sort-of-nsIRequestObserver methods */ diff --git a/image/src/imgStatusTracker.cpp b/image/src/imgStatusTracker.cpp index 5eb084f177b6..fb75c167b173 100644 --- a/image/src/imgStatusTracker.cpp +++ b/image/src/imgStatusTracker.cpp @@ -165,6 +165,18 @@ public: } } + virtual void OnUnlockedDraw() + { + NS_ABORT_IF_FALSE(mTracker->GetImage(), + "OnUnlockedDraw callback before we've created our image"); + mTracker->RecordUnlockedDraw(); + + nsTObserverArray::ForwardIterator iter(mTracker->mConsumers); + while (iter.HasMore()) { + mTracker->SendUnlockedDraw(iter.GetNext()); + } + } + virtual void OnImageIsAnimated() { NS_ABORT_IF_FALSE(mTracker->GetImage(), @@ -568,6 +580,13 @@ imgStatusTracker::RecordDiscard() mImageStatus &= ~statusBitsToClear; } +void +imgStatusTracker::RecordUnlockedDraw() +{ + NS_ABORT_IF_FALSE(mImage, + "RecordUnlockedDraw called before we have an Image"); +} + void imgStatusTracker::SendImageIsAnimated(imgRequestProxy* aProxy) { @@ -593,6 +612,13 @@ imgStatusTracker::SendDiscard(imgRequestProxy* aProxy) aProxy->OnDiscard(); } +void +imgStatusTracker::SendUnlockedDraw(imgRequestProxy* aProxy) +{ + if (!aProxy->NotificationsDeferred()) + aProxy->OnUnlockedDraw(); +} + void imgStatusTracker::RecordFrameChanged(const nsIntRect* aDirtyRect) { diff --git a/image/src/imgStatusTracker.h b/image/src/imgStatusTracker.h index a159f6845495..9610cc2494f5 100644 --- a/image/src/imgStatusTracker.h +++ b/image/src/imgStatusTracker.h @@ -145,6 +145,8 @@ public: void SendStopDecode(imgRequestProxy* aProxy, nsresult aStatus); void RecordDiscard(); void SendDiscard(imgRequestProxy* aProxy); + void RecordUnlockedDraw(); + void SendUnlockedDraw(imgRequestProxy* aProxy); void RecordImageIsAnimated(); void SendImageIsAnimated(imgRequestProxy *aProxy); diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index a93cca2124cb..5f27d3d92bec 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -38,6 +38,7 @@ #include "nsInterfaceHashtable.h" #include "nsEventStates.h" #include "nsPresArena.h" +#include "nsIImageLoadingContent.h" class nsIContent; class nsIDocument; @@ -120,10 +121,10 @@ typedef struct CapturingContentInfo { nsIContent* mContent; } CapturingContentInfo; -// 15f6c268-e40f-42a9-a7eb-e5e10a5840a1 +// 835b3946-1a4f-4132-b3ce-2e2e8be377c8 #define NS_IPRESSHELL_IID \ -{ 0x15f6c268, 0xe40f, 0x42a9, \ - { 0xa7, 0xeb, 0xe5, 0xe1, 0x0a, 0x58, 0x40, 0xa1 } } +{ 0x835b3946, 0x1a4f, 0x4132, \ + { 0xb3, 0xce, 0x2e, 0x2e, 0x8b, 0xe3, 0x77, 0xc8 } } // debug VerifyReflow flags #define VERIFY_REFLOW_ON 0x01 @@ -1327,6 +1328,9 @@ public: // with images that are in the display list aList. virtual void RebuildImageVisibility(const nsDisplayList& aList) = 0; + // Ensures the image is in the list of visible images. + virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage) = 0; + /** * Refresh observer management. */ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 61d50f8bbaf5..e3146acc38bd 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5415,6 +5415,27 @@ PresShell::ScheduleImageVisibilityUpdate() } } +void +PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage) +{ +#ifdef DEBUG + // if it has a frame make sure its in this presshell + nsCOMPtr content = do_QueryInterface(aImage); + if (content) { + PresShell* shell = static_cast(content->OwnerDoc()->GetShell()); + MOZ_ASSERT(!shell || shell == this, "wrong shell"); + } +#endif + + // This check could be slow. + if (mVisibleImages.Contains(aImage)) { + return; + } + + mVisibleImages.AppendElement(aImage); + aImage->IncrementVisibleCount(); +} + class nsAutoNotifyDidPaint { public: diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index 290473f324a7..6ed0f45fbe5e 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -339,6 +339,8 @@ public: virtual void RebuildImageVisibility(const nsDisplayList& aList); + virtual void EnsureImageInVisibleList(nsIImageLoadingContent* aImage); + protected: virtual ~PresShell(); From 9e119b46ebf4430b63e897454e20e3ef39beffaf Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:23 -0600 Subject: [PATCH 13/66] Bug 689623. Part 9. Make images default to visible when they have a frame created for them. r=mats --- content/base/src/nsImageLoadingContent.cpp | 12 ++++++++++-- layout/base/nsPresShell.cpp | 9 ++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index dcc3d7fcc144..b5d9c219fa17 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -237,6 +237,11 @@ nsImageLoadingContent::OnStopRequest(imgIRequest* aRequest, void nsImageLoadingContent::OnUnlockedDraw() { + if (mVisibleCount > 0) { + // We should already be marked as visible, there is nothing more we can do. + return; + } + nsPresContext* presContext = GetFramePresContext(); if (!presContext) return; @@ -395,6 +400,11 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame) { NS_ASSERTION(aFrame, "aFrame is null"); + nsPresContext* presContext = aFrame->PresContext(); + if (mVisibleCount == 0) { + presContext->PresShell()->EnsureImageInVisibleList(this); + } + // We pass the SKIP_FRAME_CHECK flag to TrackImage here because our primary // frame pointer hasn't been setup yet when this is caled. TrackImage(mCurrentRequest, SKIP_FRAME_CHECK); @@ -402,8 +412,6 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame) // We need to make sure that our image request is registered, if it should // be registered. - nsPresContext* presContext = aFrame->PresContext(); - if (mCurrentRequest) { nsLayoutUtils::RegisterImageRequestIfAnimated(presContext, mCurrentRequest, &mCurrentRequestRegistered); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index e3146acc38bd..d3185b5c707d 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5427,11 +5427,10 @@ PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage) } #endif - // This check could be slow. - if (mVisibleImages.Contains(aImage)) { - return; - } - + // nsImageLoadingContent doesn't call this function if it has a positive + // visible count so the image shouldn't be in mVisibleImages. Either way it + // doesn't hurt to put it in multiple times. + MOZ_ASSERT(!mVisibleImages.Contains(aImage), "image already in the array"); mVisibleImages.AppendElement(aImage); aImage->IncrementVisibleCount(); } From 34cb04e160e941832ae716d25bae15e82862dcfd Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:23 -0600 Subject: [PATCH 14/66] Bug 689623. Part 10. Make all images visible in print, print preview, chrome, xul, and resource docs and don't try to keep track of which are visible or not. r=mats --- layout/base/nsPresShell.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index d3185b5c707d..ea72d7f6012d 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5386,10 +5386,29 @@ PresShell::UpdateImageVisibility() list.DeleteAll(); } +static bool +AssumeAllImagesVisible(nsPresContext* aPresContext, nsIDocument* aDocument) +{ + if (!aPresContext || !aDocument) + return true; + + // We assume all images are visible in print, print preview, chrome, xul, and + // resource docs and don't keep track of them. + if (aPresContext->Type() == nsPresContext::eContext_PrintPreview || + aPresContext->Type() == nsPresContext::eContext_Print || + aPresContext->IsChrome() || + aDocument->IsResourceDoc() || + aDocument->IsXUL()) { + return true; + } + + return false; +} + void PresShell::ScheduleImageVisibilityUpdate() { - if (!mPresContext) + if (AssumeAllImagesVisible(mPresContext, mDocument)) return; if (!mPresContext->IsRootContentDocument()) { @@ -5418,6 +5437,11 @@ PresShell::ScheduleImageVisibilityUpdate() void PresShell::EnsureImageInVisibleList(nsIImageLoadingContent* aImage) { + if (AssumeAllImagesVisible(mPresContext, mDocument)) { + aImage->IncrementVisibleCount(); + return; + } + #ifdef DEBUG // if it has a frame make sure its in this presshell nsCOMPtr content = do_QueryInterface(aImage); From 088889359ad60191376bcad692a41f268f22a095 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:23 -0600 Subject: [PATCH 15/66] Bug 689623. Part 11. Mark SVG feImage elements as visible because they don't create display items. r=mats --- layout/svg/SVGFEImageFrame.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/layout/svg/SVGFEImageFrame.cpp b/layout/svg/SVGFEImageFrame.cpp index 7d5140dd00d9..05cc48eee5f9 100644 --- a/layout/svg/SVGFEImageFrame.cpp +++ b/layout/svg/SVGFEImageFrame.cpp @@ -88,6 +88,7 @@ SVGFEImageFrame::DestroyFrom(nsIFrame* aDestructRoot) if (imageLoader) { imageLoader->FrameDestroyed(this); + imageLoader->DecrementVisibleCount(); } SVGFEImageFrameBase::DestroyFrom(aDestructRoot); @@ -108,6 +109,8 @@ SVGFEImageFrame::Init(nsIContent* aContent, if (imageLoader) { imageLoader->FrameCreated(this); + // We assume that feImage's are always visible. + imageLoader->IncrementVisibleCount(); } return NS_OK; From 8453dd3a2631bdd849eb587dfb891264012c6596 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:23 -0600 Subject: [PATCH 16/66] Bug 689623. Part 12. Assume images in popups are visible. r=mats --- content/base/src/nsImageLoadingContent.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/content/base/src/nsImageLoadingContent.cpp b/content/base/src/nsImageLoadingContent.cpp index b5d9c219fa17..8445745d2dc4 100644 --- a/content/base/src/nsImageLoadingContent.cpp +++ b/content/base/src/nsImageLoadingContent.cpp @@ -400,6 +400,11 @@ nsImageLoadingContent::FrameCreated(nsIFrame* aFrame) { NS_ASSERTION(aFrame, "aFrame is null"); + if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { + // Assume all images in popups are visible. + IncrementVisibleCount(); + } + nsPresContext* presContext = aFrame->PresContext(); if (mVisibleCount == 0) { presContext->PresShell()->EnsureImageInVisibleList(this); @@ -443,6 +448,12 @@ nsImageLoadingContent::FrameDestroyed(nsIFrame* aFrame) UntrackImage(mCurrentRequest); UntrackImage(mPendingRequest); + + if (aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP)) { + // We assume all images in popups are visible, so this decrement balances + // out the increment in FrameCreated above. + DecrementVisibleCount(); + } } int32_t From a82d679a612405975c3afd07a18ea25ad36b5ef2 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Sun, 24 Feb 2013 18:59:24 -0600 Subject: [PATCH 17/66] Bug 689623. Part 13. Disable image visibility pass on mobile and b2g. r=mats --- b2g/app/b2g.js | 2 ++ layout/base/nsPresShell.cpp | 12 +++++++++++- mobile/android/app/mobile.js | 2 ++ mobile/xul/app/mobile.js | 2 ++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index ffbae3a0d7de..78b5b480204a 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -635,6 +635,8 @@ pref("memory.free_dirty_pages", true); pref("wap.UAProf.url", ""); pref("wap.UAProf.tagname", "x-wap-profile"); +pref("layout.imagevisibility.enabled", false); + // Enable native identity (persona/browserid) pref("dom.identity.enabled", true); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index ea72d7f6012d..0ef2042e9099 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5389,7 +5389,17 @@ PresShell::UpdateImageVisibility() static bool AssumeAllImagesVisible(nsPresContext* aPresContext, nsIDocument* aDocument) { - if (!aPresContext || !aDocument) + static bool sImageVisibilityEnabled = true; + static bool sImageVisibilityPrefCached = false; + + if (!sImageVisibilityPrefCached) { + Preferences::AddBoolVarCache(&sImageVisibilityEnabled, + "layout.imagevisibility.enabled", true); + sImageVisibilityPrefCached = true; + } + + + if (!sImageVisibilityEnabled || !aPresContext || !aDocument) return true; // We assume all images are visible in print, print preview, chrome, xul, and diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index e836b9bb311a..39daf44b1717 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -675,6 +675,8 @@ pref("app.orientation.default", ""); // back to the system. pref("memory.free_dirty_pages", true); +pref("layout.imagevisibility.enabled", false); + #ifdef MOZ_PKG_SPECIAL // Disable webgl on ARMv6 because running the reftests takes // too long for some reason (bug 843738) diff --git a/mobile/xul/app/mobile.js b/mobile/xul/app/mobile.js index 397a74242070..e7a23a07a977 100644 --- a/mobile/xul/app/mobile.js +++ b/mobile/xul/app/mobile.js @@ -657,3 +657,5 @@ pref("media.realtime_decoder.enabled", true); // Mobile manages state by autodetection pref("network.manage-offline-status", true); + +pref("layout.imagevisibility.enabled", false); From fd82e6ad894fad4f601f1068947021b9e4970e92 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Mon, 25 Feb 2013 12:20:42 +0900 Subject: [PATCH 18/66] Bug 774627 - Remove d2d pragma lib directives from MetroWidget. r=jimm --- toolkit/library/Makefile.in | 2 +- widget/windows/winrt/MetroApp.cpp | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index 9c576b92a986..c9cf15902a24 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -610,7 +610,7 @@ ifdef ACCESSIBILITY OS_LIBS += $(call EXPAND_LIBNAME,oleacc) endif ifdef MOZ_METRO -OS_LIBS += $(call EXPAND_LIBNAME,uiautomationcore) +OS_LIBS += $(call EXPAND_LIBNAME,uiautomationcore runtimeobject) endif ifdef _MSC_VER OS_LIBS += $(call EXPAND_LIBNAME,delayimp) diff --git a/widget/windows/winrt/MetroApp.cpp b/widget/windows/winrt/MetroApp.cpp index 13a758514ff7..2fff0e866432 100644 --- a/widget/windows/winrt/MetroApp.cpp +++ b/widget/windows/winrt/MetroApp.cpp @@ -20,12 +20,6 @@ using namespace ABI::Windows::Foundation; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; -// XXX move -#pragma comment(lib, "dwrite.lib") -#pragma comment(lib, "d2d1.lib") -#pragma comment(lib, "d3d11.lib") -#pragma comment(lib, "runtimeobject.lib") - // Metro specific XRE methods we call from here on an // appropriate thread. extern nsresult XRE_metroStartup(); From 1f78cb58456e2862b01b43adb9bf58c963243865 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Sun, 24 Feb 2013 22:40:42 -0500 Subject: [PATCH 19/66] Bug 844322. Make plug-in getters and setters JSPROP_SHARED so they don't get shadowed by expandos on the DOM nodes. r=jst --- dom/plugins/base/nsJSNPRuntime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index ad9dee043d2a..2a91aae43564 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -1621,7 +1621,7 @@ NPObjWrapper_NewResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsign NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id), "id must be either string or int!\n"); if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nullptr, - nullptr, JSPROP_ENUMERATE)) { + nullptr, JSPROP_ENUMERATE | JSPROP_SHARED)) { return JS_FALSE; } From 012b7c3066cbb3d473ef483e3701985302f396f3 Mon Sep 17 00:00:00 2001 From: Reuben Morais Date: Sun, 24 Feb 2013 19:53:55 -0800 Subject: [PATCH 20/66] Bug 844274 - Move contacts API getAll cache to child process. r=sicking --- dom/contacts/ContactManager.js | 44 ++++-- dom/contacts/fallback/ContactDB.jsm | 149 ++++++++----------- dom/contacts/fallback/ContactService.jsm | 26 +--- dom/contacts/tests/test_contacts_getall.html | 7 +- 4 files changed, 95 insertions(+), 131 deletions(-) diff --git a/dom/contacts/ContactManager.js b/dom/contacts/ContactManager.js index 2f5518e885b7..72197818bbcf 100644 --- a/dom/contacts/ContactManager.js +++ b/dom/contacts/ContactManager.js @@ -390,6 +390,14 @@ ContactManager.prototype = { return contacts; }, + _fireSuccessOrDone: function(aCursor, aResult) { + if (aResult == null) { + Services.DOMRequest.fireDone(aCursor); + } else { + Services.DOMRequest.fireSuccess(aCursor, aResult); + } + }, + receiveMessage: function(aMessage) { if (DEBUG) debug("receiveMessage: " + aMessage.name); let msg = aMessage.json; @@ -407,12 +415,15 @@ ContactManager.prototype = { } break; case "Contacts:GetAll:Next": - let cursor = this._cursorData[msg.cursorId]; + let data = this._cursorData[msg.cursorId]; let contact = msg.contact ? this._convertContact(msg.contact) : null; - if (contact == null) { - Services.DOMRequest.fireDone(cursor); + if (data.waitingForNext) { + if (DEBUG) debug("cursor waiting for contact, sending"); + data.waitingForNext = false; + this._fireSuccessOrDone(data.cursor, contact); } else { - Services.DOMRequest.fireSuccess(cursor, contact); + if (DEBUG) debug("cursor not waiting, saving"); + data.cachedContacts.push(contact); } break; case "Contacts:GetSimContacts:Return:OK": @@ -591,12 +602,16 @@ ContactManager.prototype = { createCursor: function CM_createCursor(aRequest) { let id = this._getRandomId(); - let cursor = Services.DOMRequest.createCursor(this._window, function() { - this.handleContinue(id); - }.bind(this)); + let data = { + cursor: Services.DOMRequest.createCursor(this._window, function() { + this.handleContinue(id); + }.bind(this)), + cachedContacts: [], + waitingForNext: true, + }; if (DEBUG) debug("saved cursor id: " + id); - this._cursorData[id] = cursor; - return [id, cursor]; + this._cursorData[id] = data; + return [id, data.cursor]; }, getAll: function CM_getAll(aOptions) { @@ -612,9 +627,14 @@ ContactManager.prototype = { handleContinue: function CM_handleContinue(aCursorId) { if (DEBUG) debug("handleContinue: " + aCursorId); - cpmm.sendAsyncMessage("Contacts:GetAll:Continue", { - cursorId: aCursorId - }); + let data = this._cursorData[aCursorId]; + if (data.cachedContacts.length > 0) { + if (DEBUG) debug("contact in cache"); + this._fireSuccessOrDone(data.cursor, data.cachedContacts.shift()); + } else { + if (DEBUG) debug("waiting for contact"); + data.waitingForNext = true; + } }, remove: function removeContact(aRecord) { diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index 6d83f447dd10..06e8e9bc32d8 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -30,8 +30,6 @@ this.ContactDB = function ContactDB(aGlobal) { ContactDB.prototype = { __proto__: IndexedDBHelper.prototype, - cursorData: {}, - upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) { if (DEBUG) debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!"); let db = aDb; @@ -472,82 +470,81 @@ ContactDB.prototype = { }, aSuccessCb, aErrorCb); }, - getObjectById: function CDB_getObjectById(aStore, aObjectId, aCallback) { - if (DEBUG) debug("getObjectById: " + aStore + ":" + aObjectId); - this.newTxn("readonly", aStore, function (txn, store) { - let req = store.get(aObjectId); - req.onsuccess = function (event) { - aCallback(event.target.result); - }; - req.onerror = function (event) { - aCallback(null); - }; - }); - }, - - getCacheForQuery: function CDB_getCacheForQuery(aQuery, aCursorId, aSuccessCb) { - if (DEBUG) debug("getCacheForQuery"); - // Here we try to get the cached results for query `aQuery'. If they don't - // exist, it means the cache was invalidated and needs to be recreated, so - // we do that. Otherwise, we just return the existing cache. - this.getObjectById(SAVED_GETALL_STORE_NAME, aQuery, function (aCache) { - if (!aCache) { - if (DEBUG) debug("creating cache for query " + aQuery); - this.createCacheForQuery(aQuery, aCursorId, aSuccessCb); - } else { - if (DEBUG) debug("cache exists"); - if (!this.cursorData[aCursorId]) { - this.cursorData[aCursorId] = aCache; - } - aSuccessCb(aCache); - } - }.bind(this)); - }, - - setCacheForQuery: function CDB_setCacheForQuery(aQuery, aCache, aCallback) { - this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function (txn, store) { - let req = store.put(aCache, aQuery); - if (!aCallback) { - return; - } - req.onsuccess = function () { - aCallback(true); - }; - req.onerror = function () { - aCallback(false); - }; - }); - }, - - createCacheForQuery: function CDB_createCacheForQuery(aQuery, aCursorId, aSuccessCb, aFailureCb) { + createCacheForQuery: function CDB_createCacheForQuery(aQuery, aSuccessCb, aFailureCb) { this.find(function (aContacts) { if (aContacts) { let contactsArray = []; for (let i in aContacts) { - contactsArray.push(aContacts[i].id); + contactsArray.push(aContacts[i]); } - this.setCacheForQuery(aQuery, contactsArray); - this.cursorData[aCursorId] = contactsArray; - aSuccessCb(contactsArray); + // save contact ids in cache + this.newTxn("readwrite", SAVED_GETALL_STORE_NAME, function(txn, store) { + store.put(contactsArray.map(function(el) el.id), aQuery); + }); + + // send full contacts + aSuccessCb(contactsArray, true); } else { - aSuccessCb(null); + aSuccessCb([], true); } }.bind(this), function (aErrorMsg) { aFailureCb(aErrorMsg); }, JSON.parse(aQuery)); }, - getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions, aCursorId) { - // Recreate the cache for this query if needed + getCacheForQuery: function CDB_getCacheForQuery(aQuery, aSuccessCb) { + if (DEBUG) debug("getCacheForQuery"); + // Here we try to get the cached results for query `aQuery'. If they don't + // exist, it means the cache was invalidated and needs to be recreated, so + // we do that. Otherwise, we just return the existing cache. + this.newTxn("readonly", SAVED_GETALL_STORE_NAME, function(txn, store) { + let req = store.get(aQuery); + req.onsuccess = function(e) { + if (e.target.result) { + if (DEBUG) debug("cache exists"); + debug("sending: " + JSON.stringify(e.target.result)); + aSuccessCb(e.target.result, false); + } else { + if (DEBUG) debug("creating cache for query " + aQuery); + this.createCacheForQuery(aQuery, aSuccessCb); + } + }.bind(this); + req.onerror = function() { + + }; + }.bind(this)); + }, + + getAll: function CDB_getAll(aSuccessCb, aFailureCb, aOptions) { let optionStr = JSON.stringify(aOptions); - this.getCacheForQuery(optionStr, aCursorId, function (aCachedResults) { + this.getCacheForQuery(optionStr, function(aCachedResults, aFullContacts) { + // aFullContacts is true if the cache didn't exist and had to be created. + // In that case, we receive the full contacts since we already have them + // in memory to create the cache anyway. This allows us to avoid accessing + // the main object store again. if (aCachedResults && aCachedResults.length > 0) { if (DEBUG) debug("query returned at least one contact"); - this.getObjectById(STORE_NAME, aCachedResults[0], function (aContact) { - this.cursorData[aCursorId].shift(); - aSuccessCb(aContact); - }.bind(this)); + if (aFullContacts) { + if (DEBUG) debug("full contacts: " + aCachedResults.length); + for (let i = 0; i < aCachedResults.length; ++i) { + aSuccessCb(aCachedResults[i]); + } + aSuccessCb(null); + } else { + let count = 0; + this.newTxn("readonly", STORE_NAME, function(txn, store) { + for (let i = 0; i < aCachedResults.length; ++i) { + store.get(aCachedResults[i]).onsuccess = function(e) { + aSuccessCb(e.target.result); + count++; + if (count == aCachedResults.length) { + aSuccessCb(null); + } + }; + } + }); + } } else { // no contacts if (DEBUG) debug("query returned no contacts"); aSuccessCb(null); @@ -555,34 +552,6 @@ ContactDB.prototype = { }.bind(this)); }, - getNext: function CDB_getNext(aSuccessCb, aFailureCb, aCursorId) { - if (DEBUG) debug("ContactDB:getNext: " + aCursorId); - let aCachedResults = this.cursorData[aCursorId]; - if (DEBUG) debug("got transient cache"); - if (aCachedResults.length > 0) { - this.getObjectById(STORE_NAME, aCachedResults[0], function(aContact) { - this.cursorData[aCursorId].shift(); - if (aContact) { - aSuccessCb(aContact); - } else { - // If the contact ID in cache is invalid, it was removed recently and - // the cache hasn't been updated to reflect the change, so we skip it. - if (DEBUG) debug("invalid contact in cache: " + aCachedResults[0]); - return this.getNext(aSuccessCb, aFailureCb, aCursorId); - } - }.bind(this)); - } else { // last contact - delete this.cursorData[aCursorId]; - aSuccessCb(null); - } - }, - - releaseCursors: function CDB_releaseCursors(aCursors) { - for (let i of aCursors) { - delete this.cursorData[i]; - } - }, - /* * Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName. * If 2 entries have the same sortyBy field or no sortBy field is present, we continue diff --git a/dom/contacts/fallback/ContactService.jsm b/dom/contacts/fallback/ContactService.jsm index 452f3717dbc4..a9e15926b5e2 100644 --- a/dom/contacts/fallback/ContactService.jsm +++ b/dom/contacts/fallback/ContactService.jsm @@ -37,12 +37,9 @@ XPCOMUtils.defineLazyGetter(this, "mRIL", function () { let myGlobal = this; this.DOMContactManager = { - // maps children to their live cursors so we can cleanup on shutdown/crash - _liveCursors: {}, - init: function() { if (DEBUG) debug("Init"); - this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:GetAll:Continue", "Contacts:Clear", "Contact:Save", + this._messages = ["Contacts:Find", "Contacts:GetAll", "Contacts:Clear", "Contact:Save", "Contact:Remove", "Contacts:GetSimContacts", "Contacts:RegisterForMessages", "child-process-shutdown"]; this._children = []; @@ -119,23 +116,6 @@ this.DOMContactManager = { }, function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); }, msg.findOptions, msg.cursorId); - if (Array.isArray(this._liveCursors[mm])) { - this._liveCursors[mm].push(msg.cursorId); - } else { - this._liveCursors[mm] = [msg.cursorId]; - } - break; - case "Contacts:GetAll:Continue": - this._db.getNext( - function(aContact) { - if (aContact == null) { // last contact, release the cursor - let cursors = this._liveCursors[mm]; - cursors.splice(cursors.indexOf(msg.cursorId), 1); - } - mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contact: aContact}); - }.bind(this), - function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); }, - msg.cursorId); break; case "Contact:Save": if (msg.options.reason === "create") { @@ -210,10 +190,6 @@ this.DOMContactManager = { break; case "child-process-shutdown": if (DEBUG) debug("Unregister"); - if (this._liveCursors[mm]) { - this._db.releaseCursors(this._liveCursors[mm]); - delete this._liveCursors[mm]; - } let index = this._children.indexOf(mm); if (index != -1) { if (DEBUG) debug("Unregister index: " + index); diff --git a/dom/contacts/tests/test_contacts_getall.html b/dom/contacts/tests/test_contacts_getall.html index 55ac7006d54b..5f132e69033f 100644 --- a/dom/contacts/tests/test_contacts_getall.html +++ b/dom/contacts/tests/test_contacts_getall.html @@ -266,7 +266,7 @@ let steps = [ add20Contacts, function() { - ok(true, "Test cache invalidation between getAll and getNext"); + ok(true, "Test cache consistency when deleting contact during getAll"); req = mozContacts.find({}); req.onsuccess = function(e) { let lastContact = e.target.result[e.target.result.length-1]; @@ -290,8 +290,7 @@ let steps = [ count++; req.continue(); } else { - is(count, 19, "19 contacts returned"); - ok(true, "last contact"); + is(count, 20, "last contact - 20 contacts returned"); next(); } } @@ -303,7 +302,7 @@ let steps = [ add20Contacts, function() { - ok(true, "Delete the currect contact while iterating"); + ok(true, "Delete the current contact while iterating"); req = mozContacts.getAll({}); let count = 0; let previousId = null; From ff8f96a75a759c0d2b9f313f8c371406916230f9 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:05 +0900 Subject: [PATCH 21/66] Bug 840409 part.1 Implement widget::IMEHandler with Initialize() and Terminate() methods r=jimm --- widget/windows/Makefile.in | 1 + widget/windows/WinIMEHandler.cpp | 51 ++++++++++++++++++++++++++++++++ widget/windows/WinIMEHandler.h | 35 ++++++++++++++++++++++ widget/windows/nsTextStore.h | 5 ++++ widget/windows/nsWindow.cpp | 13 ++------ 5 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 widget/windows/WinIMEHandler.cpp create mode 100644 widget/windows/WinIMEHandler.h diff --git a/widget/windows/Makefile.in b/widget/windows/Makefile.in index a6b2f216c38a..acf67f15ad95 100644 --- a/widget/windows/Makefile.in +++ b/widget/windows/Makefile.in @@ -52,6 +52,7 @@ CPPSRCS = \ nsWidgetFactory.cpp \ WinUtils.cpp \ WinMouseScrollHandler.cpp \ + WinIMEHandler.cpp \ $(NULL) ifdef MOZ_CRASHREPORTER diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp new file mode 100644 index 000000000000..448b20b9220c --- /dev/null +++ b/widget/windows/WinIMEHandler.cpp @@ -0,0 +1,51 @@ +/* -*- 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 "WinIMEHandler.h" +#include "nsIMM32Handler.h" + +#ifdef NS_ENABLE_TSF +#include "nsTextStore.h" +#endif // #ifdef NS_ENABLE_TSF + +namespace mozilla { +namespace widget { + +/****************************************************************************** + * IMEHandler + ******************************************************************************/ + +#ifdef NS_ENABLE_TSF +bool IMEHandler::sIsInTSFMode = false; +#endif // #ifdef NS_ENABLE_TSF + +// static +void +IMEHandler::Initialize() +{ +#ifdef NS_ENABLE_TSF + nsTextStore::Initialize(); + sIsInTSFMode = nsTextStore::IsInTSFMode(); +#endif // #ifdef NS_ENABLE_TSF + + nsIMM32Handler::Initialize(); +} + +// static +void +IMEHandler::Terminate() +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + nsTextStore::Terminate(); + sIsInTSFMode = false; + } +#endif // #ifdef NS_ENABLE_TSF + + nsIMM32Handler::Terminate(); +} + +} // namespace widget +} // namespace mozilla diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h new file mode 100644 index 000000000000..527a52e1df7d --- /dev/null +++ b/widget/windows/WinIMEHandler.h @@ -0,0 +1,35 @@ +/* -*- 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/. */ + +#ifndef WinIMEHandler_h_ +#define WinIMEHandler_h_ + +#include "nscore.h" + +namespace mozilla { +namespace widget { + +/** + * IMEHandler class is a mediator class. On Windows, there are two IME API + * sets: One is IMM which is legacy API set. The other is TSF which is modern + * API set. By using this class, non-IME handler classes don't need to worry + * that we're in which mode. + */ +class IMEHandler MOZ_FINAL +{ +public: + static void Initialize(); + static void Terminate(); + +private: +#ifdef NS_ENABLE_TSF + static bool sIsInTSFMode; +#endif // #ifdef NS_ENABLE_TSF +}; + +} // namespace widget +} // namespace mozilla + +#endif // #ifndef WinIMEHandler_h_ diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 713f0d956e14..19feb7689368 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -163,6 +163,11 @@ public: return (void*) & sDisplayAttrMgr; } + static bool IsInTSFMode() + { + return sTsfThreadMgr != nullptr; + } + protected: nsTextStore(); ~nsTextStore(); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 912c51b0d21c..738cde7b7a8d 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -169,6 +169,7 @@ #include "mozilla/HangMonitor.h" #include "nsIMM32Handler.h" +#include "WinIMEHandler.h" using namespace mozilla::widget; using namespace mozilla::layers; @@ -362,11 +363,7 @@ nsWindow::nsWindow() : nsWindowBase() // WinTaskbar.cpp for details. mozilla::widget::WinTaskbar::RegisterAppUserModelID(); gKbdLayout.LoadLayout(::GetKeyboardLayout(0)); - // Init IME handler - nsIMM32Handler::Initialize(); -#ifdef NS_ENABLE_TSF - nsTextStore::Initialize(); -#endif + IMEHandler::Initialize(); if (SUCCEEDED(::OleInitialize(NULL))) { sIsOleInitialized = TRUE; } @@ -406,17 +403,13 @@ nsWindow::~nsWindow() // Global shutdown if (sInstanceCount == 0) { -#ifdef NS_ENABLE_TSF - nsTextStore::Terminate(); -#endif + IMEHandler::Terminate(); NS_IF_RELEASE(sCursorImgContainer); if (sIsOleInitialized) { ::OleFlushClipboard(); ::OleUninitialize(); sIsOleInitialized = FALSE; } - // delete any of the IME structures that we allocated - nsIMM32Handler::Terminate(); } NS_IF_RELEASE(mNativeDragTarget); From 9097f54412aea7297062352520d1b775899ccf4f Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:05 +0900 Subject: [PATCH 22/66] Bug 840409 part.2 Move nsIMM32Handler::IsDoingKakuteiUndo() to widget::IMEContext r=jimm --- widget/windows/WinIMEHandler.cpp | 35 +++++++++++++++++++++++++++++++ widget/windows/WinIMEHandler.h | 8 +++++++ widget/windows/nsIMM32Handler.cpp | 35 ------------------------------- widget/windows/nsIMM32Handler.h | 2 -- widget/windows/nsWindow.cpp | 2 +- 5 files changed, 44 insertions(+), 38 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 448b20b9220c..7e5863852f70 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -47,5 +47,40 @@ IMEHandler::Terminate() nsIMM32Handler::Terminate(); } +// static +bool +IMEHandler::IsDoingKakuteiUndo(HWND aWnd) +{ + // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG: + // --------------------------------------------------------------------------- + // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1) + // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK + // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0) + // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF) + // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1) + // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001) + // --------------------------------------------------------------------------- + // This doesn't match usual key message pattern such as: + // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP + // See following bugs for the detail. + // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese) + // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English) + MSG startCompositionMsg, compositionMsg, charMsg; + return ::PeekMessageW(&startCompositionMsg, aWnd, + WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION, + PM_NOREMOVE | PM_NOYIELD) && + ::PeekMessageW(&compositionMsg, aWnd, WM_IME_COMPOSITION, + WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) && + ::PeekMessageW(&charMsg, aWnd, WM_CHAR, WM_CHAR, + PM_NOREMOVE | PM_NOYIELD) && + startCompositionMsg.wParam == 0x0 && + startCompositionMsg.lParam == 0x0 && + compositionMsg.wParam == 0x0 && + compositionMsg.lParam == 0x1BF && + charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 && + startCompositionMsg.time <= compositionMsg.time && + compositionMsg.time <= charMsg.time; +} + } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 527a52e1df7d..367ced47812f 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -7,6 +7,7 @@ #define WinIMEHandler_h_ #include "nscore.h" +#include namespace mozilla { namespace widget { @@ -23,6 +24,13 @@ public: static void Initialize(); static void Terminate(); + /** + * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes + * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this + * returns true, the caller needs to be careful for processing the messages. + */ + static bool IsDoingKakuteiUndo(HWND aWnd); + private: #ifdef NS_ENABLE_TSF static bool sIsInTSFMode; diff --git a/widget/windows/nsIMM32Handler.cpp b/widget/windows/nsIMM32Handler.cpp index a50e6d7c67bc..aae7b85b9af1 100644 --- a/widget/windows/nsIMM32Handler.cpp +++ b/widget/windows/nsIMM32Handler.cpp @@ -106,41 +106,6 @@ nsIMM32Handler::IsTopLevelWindowOfComposition(nsWindow* aWindow) return WinUtils::GetTopLevelHWND(wnd, true) == aWindow->GetWindowHandle(); } -/* static */ bool -nsIMM32Handler::IsDoingKakuteiUndo(HWND aWnd) -{ - // This message pattern is "Kakutei-Undo" on ATOK and WXG. - // In this case, the message queue has following messages: - // --------------------------------------------------------------------------- - // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1) - // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK - // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0) - // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF) - // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1) - // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001) - // --------------------------------------------------------------------------- - // This message pattern does not match to the above case; - // i.e.,WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR. - // For more information of this problem: - // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese) - // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English) - MSG imeStartCompositionMsg, imeCompositionMsg, charMsg; - return ::PeekMessageW(&imeStartCompositionMsg, aWnd, - WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION, - PM_NOREMOVE | PM_NOYIELD) && - ::PeekMessageW(&imeCompositionMsg, aWnd, WM_IME_COMPOSITION, - WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) && - ::PeekMessageW(&charMsg, aWnd, WM_CHAR, WM_CHAR, - PM_NOREMOVE | PM_NOYIELD) && - imeStartCompositionMsg.wParam == 0x0 && - imeStartCompositionMsg.lParam == 0x0 && - imeCompositionMsg.wParam == 0x0 && - imeCompositionMsg.lParam == 0x1BF && - charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 && - imeStartCompositionMsg.time <= imeCompositionMsg.time && - imeCompositionMsg.time <= charMsg.time; -} - /* static */ bool nsIMM32Handler::ShouldDrawCompositionStringOurselves() { diff --git a/widget/windows/nsIMM32Handler.h b/widget/windows/nsIMM32Handler.h index fb6f53d05f38..ccdd9e002fe9 100644 --- a/widget/windows/nsIMM32Handler.h +++ b/widget/windows/nsIMM32Handler.h @@ -83,8 +83,6 @@ public: return IsComposing() && IsComposingWindow(aWindow); } - static bool IsDoingKakuteiUndo(HWND aWnd); - static bool CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage); #ifdef DEBUG diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 738cde7b7a8d..1b6d43d7c7d1 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6610,7 +6610,7 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, } if (!anyCharMessagesRemoved && DOMKeyCode == NS_VK_BACK && - nsIMM32Handler::IsDoingKakuteiUndo(mWnd)) { + IMEHandler::IsDoingKakuteiUndo(mWnd)) { NS_ASSERTION(!aFakeCharMessage, "We shouldn't be touching the real msg queue"); RemoveMessageAndDispatchPluginEvent(WM_CHAR, WM_CHAR); From ba8d3aca49f4b804e186c17a20bc53c38965251f Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:05 +0900 Subject: [PATCH 23/66] Bug 840409 part.3 Implement widget::IMEHandler::ProcessMessage() r=jimm --- widget/windows/WinIMEHandler.cpp | 23 +++++++++++++++++++++++ widget/windows/WinIMEHandler.h | 12 ++++++++++++ widget/windows/nsWindow.cpp | 9 ++------- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 7e5863852f70..c28b1b31bbb0 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -10,6 +10,8 @@ #include "nsTextStore.h" #endif // #ifdef NS_ENABLE_TSF +#include "nsWindow.h" + namespace mozilla { namespace widget { @@ -47,6 +49,27 @@ IMEHandler::Terminate() nsIMM32Handler::Terminate(); } +// static +bool +IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, + WPARAM& aWParam, LPARAM& aLParam, + LRESULT* aRetValue, bool& aEatMessage) +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + if (aMessage == WM_USER_TSF_TEXTCHANGE) { + nsTextStore::OnTextChangeMsg(); + aEatMessage = true; + return true; + } + return false; + } +#endif // #ifdef NS_ENABLE_TSF + + return nsIMM32Handler::ProcessMessage(aWindow, aMessage, aWParam, aLParam, + aRetValue, aEatMessage); +} + // static bool IMEHandler::IsDoingKakuteiUndo(HWND aWnd) diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 367ced47812f..3338e005a7cb 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -9,6 +9,8 @@ #include "nscore.h" #include +class nsWindow; + namespace mozilla { namespace widget { @@ -24,6 +26,16 @@ public: static void Initialize(); static void Terminate(); + /** + * When the message is not needed to handle anymore by the caller, this + * returns true. Otherwise, false. + * Additionally, if aEatMessage is true, the caller shouldn't call next + * wndproc anymore. + */ + static bool ProcessMessage(nsWindow* aWindow, UINT aMessage, + WPARAM& aWParam, LPARAM& aLParam, + LRESULT* aRetValue, bool& aEatMessage); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 1b6d43d7c7d1..80123d22aeb6 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -4502,8 +4502,8 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, #endif bool eatMessage; - if (nsIMM32Handler::ProcessMessage(this, msg, wParam, lParam, aRetValue, - eatMessage)) { + if (IMEHandler::ProcessMessage(this, msg, wParam, lParam, aRetValue, + eatMessage)) { return mWnd ? eatMessage : true; } @@ -5425,11 +5425,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, default: { -#ifdef NS_ENABLE_TSF - if (msg == WM_USER_TSF_TEXTCHANGE) { - nsTextStore::OnTextChangeMsg(); - } -#endif //NS_ENABLE_TSF if (msg == nsAppShell::GetTaskbarButtonCreatedMessage()) SetHasTaskbarIconBeenCreated(); if (msg == sOOPPPluginFocusEvent) { From e952bfc1d5add2b0a9839eb0d1a657cf6ec9b187 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:05 +0900 Subject: [PATCH 24/66] Bug 840409 part.4 Implement widget::IMEHandler::CurrentKeyboardLayoutHasIME() for debug r=jimm --- widget/windows/WinIMEHandler.cpp | 15 +++++++++ widget/windows/WinIMEHandler.h | 7 +++++ widget/windows/nsTextStore.cpp | 54 ++++++++++++++++++++++++++++++++ widget/windows/nsTextStore.h | 5 +++ widget/windows/nsWindow.cpp | 4 +-- 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index c28b1b31bbb0..07023f8f8a45 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -70,6 +70,21 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, aRetValue, aEatMessage); } +#ifdef DEBUG +// static +bool +IMEHandler::CurrentKeyboardLayoutHasIME() +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::CurrentKeyboardLayoutHasIME(); + } +#endif // #ifdef NS_ENABLE_TSF + + return nsIMM32Handler::IsIMEAvailable(); +} +#endif // #ifdef DEBUG + // static bool IMEHandler::IsDoingKakuteiUndo(HWND aWnd) diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 3338e005a7cb..344f864d9471 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -43,6 +43,13 @@ public: */ static bool IsDoingKakuteiUndo(HWND aWnd); +#ifdef DEBUG + /** + * Returns true when current keyboard layout has IME. Otherwise, false. + */ + static bool CurrentKeyboardLayoutHasIME(); +#endif // #ifdef DEBUG + private: #ifdef NS_ENABLE_TSF static bool sIsInTSFMode; diff --git a/widget/windows/nsTextStore.cpp b/widget/windows/nsTextStore.cpp index 4349701d8452..ccbb3c194c64 100644 --- a/widget/windows/nsTextStore.cpp +++ b/widget/windows/nsTextStore.cpp @@ -3179,3 +3179,57 @@ nsTextStore::Terminate(void) NS_RELEASE(sTsfThreadMgr); } } + +#ifdef DEBUG +// static +bool +nsTextStore::CurrentKeyboardLayoutHasIME() +{ + // XXX MSDN documents that ITfInputProcessorProfiles is available only on + // desktop apps. However, there is no known way to obtain + // ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles + // instance. + nsRefPtr profiles; + HRESULT hr = ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, + CLSCTX_INPROC_SERVER, + IID_ITfInputProcessorProfiles, + getter_AddRefs(profiles)); + if (FAILED(hr) || !profiles) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to create " + "an input processor profiles instance")); + return false; + } + nsRefPtr profileMgr; + hr = profiles->QueryInterface(IID_ITfInputProcessorProfileMgr, + getter_AddRefs(profileMgr)); + if (FAILED(hr) || !profileMgr) { + // On Windows Vista or later, ImmIsIME() API always returns true. + // If we failed to obtain the profile manager, we cannot know if current + // keyboard layout has IME. + if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to query " + "ITfInputProcessorProfileMgr")); + return false; + } + // If the profiles instance doesn't have ITfInputProcessorProfileMgr + // interface, that means probably we're running on WinXP or WinServer2003 + // (except WinServer2003 R2). Then, we should use ImmIsIME(). + return ::ImmIsIME(::GetKeyboardLayout(0)); + } + + TF_INPUTPROCESSORPROFILE profile; + hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); + if (hr == S_FALSE) { + return false; // not found or not active + } + if (FAILED(hr)) { + PR_LOG(sTextStoreLog, PR_LOG_ERROR, + ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive " + "active profile")); + return false; + } + return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR); +} +#endif // #ifdef DEBUG diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 19feb7689368..5f4f4797303a 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -168,6 +168,11 @@ public: return sTsfThreadMgr != nullptr; } +#ifdef DEBUG + // Returns true when keyboard layout has IME (TIP). + static bool CurrentKeyboardLayoutHasIME(); +#endif // #ifdef DEBUG + protected: nsTextStore(); ~nsTextStore(); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 80123d22aeb6..8645be6cd8e4 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -593,7 +593,7 @@ nsWindow::Create(nsIWidget *aParent, nsIMEContext IMEContext(mWnd); mInputContext.mNativeIMEContext = static_cast(IMEContext.get()); MOZ_ASSERT(mInputContext.mNativeIMEContext || - !nsIMM32Handler::IsIMEAvailable()); + !IMEHandler::CurrentKeyboardLayoutHasIME()); // If no IME context is available, we should set this widget's pointer since // nullptr indicates there is only one context per process on the platform. if (!mInputContext.mNativeIMEContext) { @@ -7528,7 +7528,7 @@ bool nsWindow::AssociateDefaultIMC(bool aAssociate) // Note that if IME isn't available with current keyboard layout, // IMM might not be installed on the system such as English Windows. // On such system, IMM APIs always fail. - NS_ASSERTION(ret || !nsIMM32Handler::IsIMEAvailable(), + NS_ASSERTION(ret || !IMEHandler::CurrentKeyboardLayoutHasIME(), "ImmAssociateContextEx failed to restore default IMC"); if (ret) { nsIMEContext newIMEContext(mWnd); From d4f5d5a2595b0ce07445883ffdf13697c9ffdedb Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:05 +0900 Subject: [PATCH 25/66] Bug 840409 part.5 Implement widget::IMEHandler::IsComposing() and widget::IMEHandler::IsComposingOn() r=jimm --- widget/windows/WinIMEHandler.cpp | 26 ++++++++++++++++++++++++++ widget/windows/WinIMEHandler.h | 11 +++++++++++ widget/windows/nsTextStore.h | 10 ++++++++++ widget/windows/nsWindow.cpp | 8 ++++---- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 07023f8f8a45..7ca312ec808b 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -70,6 +70,32 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, aRetValue, aEatMessage); } +// static +bool +IMEHandler::IsComposing() +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::IsComposing(); + } +#endif // #ifdef NS_ENABLE_TSF + + return nsIMM32Handler::IsComposing(); +} + +// static +bool +IMEHandler::IsComposingOn(nsWindow* aWindow) +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::IsComposingOn(aWindow); + } +#endif // #ifdef NS_ENABLE_TSF + + return nsIMM32Handler::IsComposingOn(aWindow); +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 344f864d9471..63920320be56 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -36,6 +36,17 @@ public: WPARAM& aWParam, LPARAM& aLParam, LRESULT* aRetValue, bool& aEatMessage); + /** + * When there is a composition, returns true. Otherwise, false. + */ + static bool IsComposing(); + + /** + * When there is a composition and it's in the window, returns true. + * Otherwise, false. + */ + static bool IsComposingOn(nsWindow* aWindow); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 5f4f4797303a..9fa14fff167f 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -168,6 +168,16 @@ public: return sTsfThreadMgr != nullptr; } + static bool IsComposing() + { + return (sTsfTextStore && sTsfTextStore->mCompositionView != nullptr); + } + + static bool IsComposingOn(nsWindowBase* aWidget) + { + return (IsComposing() && sTsfTextStore->mWidget == aWidget); + } + #ifdef DEBUG // Returns true when keyboard layout has IME (TIP). static bool CurrentKeyboardLayoutHasIME(); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 8645be6cd8e4..33f01e7ef6ba 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -5695,7 +5695,7 @@ LRESULT nsWindow::ProcessKeyUpMessage(const MSG &aMsg, bool *aEventDispatched) return FALSE; } - if (!nsIMM32Handler::IsComposingOn(this)) { + if (!IMEHandler::IsComposingOn(this)) { return OnKeyUp(aMsg, modKeyState, aEventDispatched); } @@ -5734,7 +5734,7 @@ LRESULT nsWindow::ProcessKeyDownMessage(const MSG &aMsg, return FALSE; LRESULT result = 0; - if (!nsIMM32Handler::IsComposingOn(this)) { + if (!IMEHandler::IsComposingOn(this)) { result = OnKeyDown(aMsg, modKeyState, aEventDispatched, nullptr); // OnKeyDown cleaned up the redirected message information itself, so, // we should do nothing. @@ -6879,7 +6879,7 @@ LRESULT nsWindow::OnChar(const MSG &aMsg, modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL); } - if (nsIMM32Handler::IsComposingOn(this)) { + if (IMEHandler::IsComposingOn(this)) { ResetInputState(); } @@ -7403,7 +7403,7 @@ nsWindow::SetInputContext(const InputContext& aContext, #ifdef NS_ENABLE_TSF nsTextStore::SetInputContext(aContext); #endif //NS_ENABLE_TSF - if (nsIMM32Handler::IsComposing()) { + if (IMEHandler::IsComposing()) { ResetInputState(); } void* nativeIMEContext = mInputContext.mNativeIMEContext; From 7eb648f847abd3828c38880766de3c656b9891b3 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 26/66] Bug 840409 part.6 Implement widget::IMEHandler::NotifyIME() for committing and canceling composition r=jimm, feedback=roc --- widget/windows/WinIMEHandler.cpp | 36 ++++++++++++++++++++++++++++++++ widget/windows/WinIMEHandler.h | 7 +++++++ widget/windows/nsTextStore.h | 2 +- widget/windows/nsWindow.cpp | 22 ++----------------- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 7ca312ec808b..d65d5560d270 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -96,6 +96,42 @@ IMEHandler::IsComposingOn(nsWindow* aWindow) return nsIMM32Handler::IsComposingOn(aWindow); } +// static +nsresult +IMEHandler::NotifyIME(nsWindow* aWindow, + NotificationToIME aNotification) +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + switch (aNotification) { + case REQUEST_TO_COMMIT_COMPOSITION: + if (nsTextStore::IsComposingOn(aWindow)) { + nsTextStore::CommitComposition(false); + } + return NS_OK; + case REQUEST_TO_CANCEL_COMPOSITION: + if (nsTextStore::IsComposingOn(aWindow)) { + nsTextStore::CommitComposition(true); + } + return NS_OK; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } + } +#endif //NS_ENABLE_TSF + + switch (aNotification) { + case REQUEST_TO_COMMIT_COMPOSITION: + nsIMM32Handler::CommitComposition(aWindow); + return NS_OK; + case REQUEST_TO_CANCEL_COMPOSITION: + nsIMM32Handler::CancelComposition(aWindow); + return NS_OK; + default: + return NS_ERROR_NOT_IMPLEMENTED; + } +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 63920320be56..0a69208b009d 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -7,6 +7,7 @@ #define WinIMEHandler_h_ #include "nscore.h" +#include "nsEvent.h" #include class nsWindow; @@ -47,6 +48,12 @@ public: */ static bool IsComposingOn(nsWindow* aWindow); + /** + * Notifies IME of the notification (a request or an event). + */ + static nsresult NotifyIME(nsWindow* aWindow, + NotificationToIME aNotification); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 9fa14fff167f..b52bf877a901 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -101,7 +101,7 @@ public: static void CommitComposition(bool aDiscard) { - if (!sTsfTextStore) return; + NS_ENSURE_TRUE_VOID(sTsfTextStore); sTsfTextStore->CommitCompositionInternal(aDiscard); } diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 33f01e7ef6ba..2412295b072f 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7384,16 +7384,7 @@ nsWindow::OnSysColorChanged() NS_IMETHODIMP nsWindow::ResetInputState() { -#ifdef DEBUG_KBSTATE - PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("ResetInputState\n")); -#endif - -#ifdef NS_ENABLE_TSF - nsTextStore::CommitComposition(false); -#endif //NS_ENABLE_TSF - - nsIMM32Handler::CommitComposition(this); - return NS_OK; + return IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION); } NS_IMETHODIMP_(void) @@ -7462,16 +7453,7 @@ nsWindow::GetInputContext() NS_IMETHODIMP nsWindow::CancelIMEComposition() { -#ifdef DEBUG_KBSTATE - PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("CancelIMEComposition\n")); -#endif - -#ifdef NS_ENABLE_TSF - nsTextStore::CommitComposition(true); -#endif //NS_ENABLE_TSF - - nsIMM32Handler::CancelComposition(this); - return NS_OK; + return IMEHandler::NotifyIME(this, REQUEST_TO_CANCEL_COMPOSITION); } NS_IMETHODIMP From 6d5766f8baead1d11abf4baafd44928ae5ce2a26 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 27/66] Bug 840409 part.7 Define new notification for IME of focus or selection change and handle them in widget::IMEHandler::NotifyIME() r=jimm, feedback=roc --- widget/nsEvent.h | 8 ++++++++ widget/windows/WinIMEHandler.cpp | 8 ++++++++ widget/windows/nsTextStore.cpp | 4 +--- widget/windows/nsTextStore.h | 2 +- widget/windows/nsWindow.cpp | 13 ++++++------- widget/windows/nsWindow.h | 4 +++- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/widget/nsEvent.h b/widget/nsEvent.h index ad818c3188f7..0bb600b9c497 100644 --- a/widget/nsEvent.h +++ b/widget/nsEvent.h @@ -102,7 +102,15 @@ typedef uint16_t Modifiers; // NotificationToIME is shared by nsIMEStateManager and TextComposition. enum NotificationToIME { + // XXX We should replace NOTIFY_IME_OF_CURSOR_POS_CHANGED with + // NOTIFY_IME_OF_SELECTION_CHANGE later. NOTIFY_IME_OF_CURSOR_POS_CHANGED, + // An editable content is getting focus + NOTIFY_IME_OF_FOCUS, + // An editable content is losing focus + NOTIFY_IME_OF_BLUR, + // Selection in the focused editable content is changed + NOTIFY_IME_OF_SELECTION_CHANGE, REQUEST_TO_COMMIT_COMPOSITION, REQUEST_TO_CANCEL_COMPOSITION }; diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index d65d5560d270..2edeb0c1a530 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -104,6 +104,14 @@ IMEHandler::NotifyIME(nsWindow* aWindow, #ifdef NS_ENABLE_TSF if (sIsInTSFMode) { switch (aNotification) { + case NOTIFY_IME_OF_SELECTION_CHANGE: + return nsTextStore::OnSelectionChange(); + case NOTIFY_IME_OF_FOCUS: + return nsTextStore::OnFocusChange(true, aWindow, + aWindow->GetInputContext().mIMEState.mEnabled); + case NOTIFY_IME_OF_BLUR: + return nsTextStore::OnFocusChange(false, aWindow, + aWindow->GetInputContext().mIMEState.mEnabled); case REQUEST_TO_COMMIT_COMPOSITION: if (nsTextStore::IsComposingOn(aWindow)) { nsTextStore::CommitComposition(false); diff --git a/widget/windows/nsTextStore.cpp b/widget/windows/nsTextStore.cpp index ccbb3c194c64..6a26083bdc9e 100644 --- a/widget/windows/nsTextStore.cpp +++ b/widget/windows/nsTextStore.cpp @@ -2817,9 +2817,7 @@ nsTextStore::OnFocusChange(bool aGotFocus, GetIMEEnabledName(aIMEEnabled), sTsfThreadMgr, sTsfTextStore)); // no change notifications if TSF is disabled - if (!sTsfThreadMgr || !sTsfTextStore) { - return NS_ERROR_NOT_AVAILABLE; - } + NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE); if (aGotFocus) { bool bRet = sTsfTextStore->Create(aFocusedWidget, aIMEEnabled); diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index b52bf877a901..7d17a02b9061 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -133,7 +133,7 @@ public: static nsresult OnSelectionChange(void) { - if (!sTsfTextStore) return NS_OK; + NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); return sTsfTextStore->OnSelectionChangeInternal(); } diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 2412295b072f..2ba38bb49470 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7467,17 +7467,14 @@ nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState) return NS_OK; } -#ifdef NS_ENABLE_TSF NS_IMETHODIMP nsWindow::OnIMEFocusChange(bool aFocus) { - nsresult rv = nsTextStore::OnFocusChange(aFocus, this, - mInputContext.mIMEState.mEnabled); - if (rv == NS_ERROR_NOT_AVAILABLE) - rv = NS_OK; // TSF is not enabled, maybe. - return rv; + return IMEHandler::NotifyIME(this, aFocus ? NOTIFY_IME_OF_FOCUS : + NOTIFY_IME_OF_BLUR); } +#ifdef NS_ENABLE_TSF NS_IMETHODIMP nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, @@ -7485,13 +7482,15 @@ nsWindow::OnIMETextChange(uint32_t aStart, { return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd); } +#endif // #ifdef NS_ENABLE_TSF NS_IMETHODIMP nsWindow::OnIMESelectionChange(void) { - return nsTextStore::OnSelectionChange(); + return IMEHandler::NotifyIME(this, NOTIFY_IME_OF_SELECTION_CHANGE); } +#ifdef NS_ENABLE_TSF nsIMEUpdatePreference nsWindow::GetIMEUpdatePreference() { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 5fca2e5af4c8..17c0fc7ecc8c 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -176,10 +176,12 @@ public: virtual nsTransparencyMode GetTransparencyMode(); virtual void UpdateOpaqueRegion(const nsIntRegion& aOpaqueRegion); #endif // MOZ_XUL -#ifdef NS_ENABLE_TSF NS_IMETHOD OnIMEFocusChange(bool aFocus); +#ifdef NS_ENABLE_TSF NS_IMETHOD OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd); +#endif // NS_ENABLE_TSF NS_IMETHOD OnIMESelectionChange(void); +#ifdef NS_ENABLE_TSF virtual nsIMEUpdatePreference GetIMEUpdatePreference(); #endif // NS_ENABLE_TSF NS_IMETHOD GetNonClientMargins(nsIntMargin &margins); From 4559245be6aff36412ad71fdfc855c32d70467b0 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 28/66] Bug 840409 part.8 Implement widget::IMEHandler::NotifyIMEOfTextChange() r=jimm --- widget/windows/WinIMEHandler.cpp | 15 +++++++++++++++ widget/windows/WinIMEHandler.h | 7 +++++++ widget/windows/nsTextStore.h | 4 ++-- widget/windows/nsWindow.cpp | 4 +--- widget/windows/nsWindow.h | 2 -- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 2edeb0c1a530..b717da5a0bef 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -140,6 +140,21 @@ IMEHandler::NotifyIME(nsWindow* aWindow, } } +// static +nsresult +IMEHandler::NotifyIMEOfTextChange(uint32_t aStart, + uint32_t aOldEnd, + uint32_t aNewEnd) +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd); + } +#endif //NS_ENABLE_TSF + + return NS_ERROR_NOT_IMPLEMENTED; +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 0a69208b009d..9f548eab60a4 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -54,6 +54,13 @@ public: static nsresult NotifyIME(nsWindow* aWindow, NotificationToIME aNotification); + /** + * Notifies IME of text change in the focused editable content. + */ + static nsresult NotifyIMEOfTextChange(uint32_t aStart, + uint32_t aOldEnd, + uint32_t aNewEnd); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 7d17a02b9061..f677e3294758 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -119,13 +119,13 @@ public: uint32_t aOldEnd, uint32_t aNewEnd) { - if (!sTsfTextStore) return NS_OK; + NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); return sTsfTextStore->OnTextChangeInternal(aStart, aOldEnd, aNewEnd); } static void OnTextChangeMsg(void) { - if (!sTsfTextStore) return; + NS_ENSURE_TRUE_VOID(sTsfTextStore); // Notify TSF text change // (see comments on WM_USER_TSF_TEXTCHANGE in nsTextStore.h) sTsfTextStore->OnTextChangeMsgInternal(); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 2ba38bb49470..40171f65f71a 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7474,15 +7474,13 @@ nsWindow::OnIMEFocusChange(bool aFocus) NOTIFY_IME_OF_BLUR); } -#ifdef NS_ENABLE_TSF NS_IMETHODIMP nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd) { - return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd); + return IMEHandler::NotifyIMEOfTextChange(aStart, aOldEnd, aNewEnd); } -#endif // #ifdef NS_ENABLE_TSF NS_IMETHODIMP nsWindow::OnIMESelectionChange(void) diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 17c0fc7ecc8c..5f7324fe2297 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -177,9 +177,7 @@ public: virtual void UpdateOpaqueRegion(const nsIntRegion& aOpaqueRegion); #endif // MOZ_XUL NS_IMETHOD OnIMEFocusChange(bool aFocus); -#ifdef NS_ENABLE_TSF NS_IMETHOD OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd); -#endif // NS_ENABLE_TSF NS_IMETHOD OnIMESelectionChange(void); #ifdef NS_ENABLE_TSF virtual nsIMEUpdatePreference GetIMEUpdatePreference(); From 60933117b16c3088466ae77f93440239eaf4f43e Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 29/66] Bug 840409 part.9 Implement widget::IMEHandler::GetUpdatePreference() r=jimm --- widget/windows/WinIMEHandler.cpp | 13 +++++++++++++ widget/windows/WinIMEHandler.h | 6 ++++++ widget/windows/nsWindow.cpp | 5 +---- widget/windows/nsWindow.h | 2 -- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index b717da5a0bef..1f81ad23ba2e 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -155,6 +155,19 @@ IMEHandler::NotifyIMEOfTextChange(uint32_t aStart, return NS_ERROR_NOT_IMPLEMENTED; } +// static +nsIMEUpdatePreference +IMEHandler::GetUpdatePreference() +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::GetIMEUpdatePreference(); + } +#endif //NS_ENABLE_TSF + + return nsIMEUpdatePreference(false, false); +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 9f548eab60a4..58fc57c047cc 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -11,6 +11,7 @@ #include class nsWindow; +struct nsIMEUpdatePreference; namespace mozilla { namespace widget { @@ -61,6 +62,11 @@ public: uint32_t aOldEnd, uint32_t aNewEnd); + /** + * Returns update preferences. + */ + static nsIMEUpdatePreference GetUpdatePreference(); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 40171f65f71a..d53d10dbd0e6 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7488,15 +7488,12 @@ nsWindow::OnIMESelectionChange(void) return IMEHandler::NotifyIME(this, NOTIFY_IME_OF_SELECTION_CHANGE); } -#ifdef NS_ENABLE_TSF nsIMEUpdatePreference nsWindow::GetIMEUpdatePreference() { - return nsTextStore::GetIMEUpdatePreference(); + return IMEHandler::GetUpdatePreference(); } -#endif //NS_ENABLE_TSF - bool nsWindow::AssociateDefaultIMC(bool aAssociate) { nsIMEContext IMEContext(mWnd); diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 5f7324fe2297..7577e1243c62 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -179,9 +179,7 @@ public: NS_IMETHOD OnIMEFocusChange(bool aFocus); NS_IMETHOD OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd); NS_IMETHOD OnIMESelectionChange(void); -#ifdef NS_ENABLE_TSF virtual nsIMEUpdatePreference GetIMEUpdatePreference(); -#endif // NS_ENABLE_TSF NS_IMETHOD GetNonClientMargins(nsIntMargin &margins); NS_IMETHOD SetNonClientMargins(nsIntMargin &margins); void SetDrawsInTitlebar(bool aState); From d5fcbc0600e0f00972a78a5362c65a6dd4aa8c1e Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 30/66] Bug 840409 part.10 Implement widget::IMEHandler::SetOpenState() and widget::IMEHandler::GetOpenState() r=jimm --- widget/windows/WinIMEHandler.cpp | 28 ++++++++++++++++++++++++++++ widget/windows/WinIMEHandler.h | 6 ++++++ widget/windows/nsIMM32Handler.h | 16 ++++++++++++++++ widget/windows/nsWindow.cpp | 23 ++++------------------- 4 files changed, 54 insertions(+), 19 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 1f81ad23ba2e..702caa98537a 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -168,6 +168,34 @@ IMEHandler::GetUpdatePreference() return nsIMEUpdatePreference(false, false); } +void +IMEHandler::SetOpenState(nsWindow* aWindow, bool aOpen) +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + nsTextStore::SetIMEOpenState(aOpen); + return; + } +#endif //NS_ENABLE_TSF + + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + IMEContext.SetOpenState(aOpen); +} + +// static +bool +IMEHandler::GetOpenState(nsWindow* aWindow) +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::GetIMEOpenState(); + } +#endif //NS_ENABLE_TSF + + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + return IMEContext.GetOpenState(); +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 58fc57c047cc..cfacfd12cc97 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -67,6 +67,12 @@ public: */ static nsIMEUpdatePreference GetUpdatePreference(); + /** + * Sets and Gets IME open state. + */ + static void SetOpenState(nsWindow* aWindow, bool aOpen); + static bool GetOpenState(nsWindow* aWindow); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsIMM32Handler.h b/widget/windows/nsIMM32Handler.h index ccdd9e002fe9..3dd46bea62a3 100644 --- a/widget/windows/nsIMM32Handler.h +++ b/widget/windows/nsIMM32Handler.h @@ -45,6 +45,22 @@ public: return !!mIMC; } + void SetOpenState(bool aOpen) const + { + if (!mIMC) { + return; + } + ::ImmSetOpenStatus(mIMC, aOpen); + } + + bool GetOpenState() const + { + if (!mIMC) { + return false; + } + return (::ImmGetOpenStatus(mIMC) != FALSE); + } + protected: nsIMEContext() { diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index d53d10dbd0e6..0238e42a1004 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7417,13 +7417,7 @@ nsWindow::SetInputContext(const InputContext& aContext, if (enable && mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) { bool open = (mInputContext.mIMEState.mOpen == IMEState::OPEN); -#ifdef NS_ENABLE_TSF - nsTextStore::SetIMEOpenState(open); -#endif //NS_ENABLE_TSF - nsIMEContext IMEContext(mWnd); - if (IMEContext.IsValid()) { - ::ImmSetOpenStatus(IMEContext.get(), open); - } + IMEHandler::SetOpenState(this, open); } } @@ -7433,20 +7427,11 @@ nsWindow::GetInputContext() mInputContext.mIMEState.mOpen = IMEState::CLOSED; switch (mInputContext.mIMEState.mEnabled) { case IMEState::ENABLED: - case IMEState::PLUGIN: { - nsIMEContext IMEContext(mWnd); - if (IMEContext.IsValid()) { - mInputContext.mIMEState.mOpen = - ::ImmGetOpenStatus(IMEContext.get()) ? IMEState::OPEN : - IMEState::CLOSED; - } -#ifdef NS_ENABLE_TSF - if (mInputContext.mIMEState.mOpen == IMEState::CLOSED && - nsTextStore::GetIMEOpenState()) { + case IMEState::PLUGIN: + if (IMEHandler::GetOpenState(this)) { mInputContext.mIMEState.mOpen = IMEState::OPEN; } -#endif //NS_ENABLE_TSF - } + break; } return mInputContext; } From a75d2bc49fc5ba9b7cd3c4fe7f59102b0feca41d Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 31/66] Bug 840409 part.11 Implement nsIMEContext::AssociateDefaultContext() and nsIMEContext::Disassociate() r=jimm --- widget/windows/WinIMEHandler.cpp | 8 ++++++ widget/windows/WinIMEHandler.h | 5 ++++ widget/windows/nsIMM32Handler.cpp | 12 ++++---- widget/windows/nsIMM32Handler.h | 26 +++++++++++++++++ widget/windows/nsWindow.cpp | 48 +++++-------------------------- 5 files changed, 52 insertions(+), 47 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 702caa98537a..d764a11c3dd8 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -196,6 +196,14 @@ IMEHandler::GetOpenState(nsWindow* aWindow) return IMEContext.GetOpenState(); } +// static +void +IMEHandler::OnDestroyWindow(nsWindow* aWindow) +{ + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + IMEContext.AssociateDefaultContext(); +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index cfacfd12cc97..aacf7cdf24e1 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -73,6 +73,11 @@ public: static void SetOpenState(nsWindow* aWindow, bool aOpen); static bool GetOpenState(nsWindow* aWindow); + /** + * Called when the window is destroying. + */ + static void OnDestroyWindow(nsWindow* aWindow); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsIMM32Handler.cpp b/widget/windows/nsIMM32Handler.cpp index aae7b85b9af1..ee9e16bbd572 100644 --- a/widget/windows/nsIMM32Handler.cpp +++ b/widget/windows/nsIMM32Handler.cpp @@ -214,19 +214,19 @@ nsIMM32Handler::CommitComposition(nsWindow* aWindow, bool aForce) return; } - bool associated = aWindow->AssociateDefaultIMC(true); + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + bool associated = IMEContext.AssociateDefaultContext(); PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: CommitComposition, associated=%s\n", associated ? "YES" : "NO")); - nsIMEContext IMEContext(aWindow->GetWindowHandle()); if (IMEContext.IsValid()) { ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0); ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0); } if (associated) { - aWindow->AssociateDefaultIMC(false); + IMEContext.Disassociate(); } } @@ -245,18 +245,18 @@ nsIMM32Handler::CancelComposition(nsWindow* aWindow, bool aForce) return; } - bool associated = aWindow->AssociateDefaultIMC(true); + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + bool associated = IMEContext.AssociateDefaultContext(); PR_LOG(gIMM32Log, PR_LOG_ALWAYS, ("IMM32: CancelComposition, associated=%s\n", associated ? "YES" : "NO")); - nsIMEContext IMEContext(aWindow->GetWindowHandle()); if (IMEContext.IsValid()) { ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0); } if (associated) { - aWindow->AssociateDefaultIMC(false); + IMEContext.Disassociate(); } } diff --git a/widget/windows/nsIMM32Handler.h b/widget/windows/nsIMM32Handler.h index 3dd46bea62a3..b0f09d46c9cc 100644 --- a/widget/windows/nsIMM32Handler.h +++ b/widget/windows/nsIMM32Handler.h @@ -61,6 +61,32 @@ public: return (::ImmGetOpenStatus(mIMC) != FALSE); } + bool AssociateDefaultContext() + { + // We assume that there is only default IMC, no new IMC has been created. + if (mIMC) { + return false; + } + if (!::ImmAssociateContextEx(mWnd, NULL, IACE_DEFAULT)) { + return false; + } + mIMC = ::ImmGetContext(mWnd); + return (mIMC != NULL); + } + + bool Disassociate() + { + if (!mIMC) { + return false; + } + if (!::ImmAssociateContextEx(mWnd, NULL, 0)) { + return false; + } + ::ImmReleaseContext(mWnd, mIMC); + mIMC = NULL; + return true; + } + protected: nsIMEContext() { diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 0238e42a1004..90aeee137849 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7200,8 +7200,7 @@ void nsWindow::OnDestroy() CaptureRollupEvents(nullptr, false); } - // Restore the IM context. - AssociateDefaultIMC(true); + IMEHandler::OnDestroyWindow(this); // Turn off mouse trails if enabled. MouseTrailer* mtrailer = nsToolkit::gMouseTrailer; @@ -7403,12 +7402,15 @@ nsWindow::SetInputContext(const InputContext& aContext, bool enable = (mInputContext.mIMEState.mEnabled == IMEState::ENABLED || mInputContext.mIMEState.mEnabled == IMEState::PLUGIN); - AssociateDefaultIMC(enable); - + nsIMEContext IMEContext(mWnd); if (enable) { - nsIMEContext IMEContext(mWnd); + IMEContext.AssociateDefaultContext(); mInputContext.mNativeIMEContext = static_cast(IMEContext.get()); + } else if (!mOnDestroyCalled) { + // Don't disassociate the context after the window is destroyed. + IMEContext.Disassociate(); } + // Restore the latest associated context when we cannot get actual context. if (!mInputContext.mNativeIMEContext) { mInputContext.mNativeIMEContext = nativeIMEContext; @@ -7479,42 +7481,6 @@ nsWindow::GetIMEUpdatePreference() return IMEHandler::GetUpdatePreference(); } -bool nsWindow::AssociateDefaultIMC(bool aAssociate) -{ - nsIMEContext IMEContext(mWnd); - - if (aAssociate) { - BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, IACE_DEFAULT); -#ifdef DEBUG - // Note that if IME isn't available with current keyboard layout, - // IMM might not be installed on the system such as English Windows. - // On such system, IMM APIs always fail. - NS_ASSERTION(ret || !IMEHandler::CurrentKeyboardLayoutHasIME(), - "ImmAssociateContextEx failed to restore default IMC"); - if (ret) { - nsIMEContext newIMEContext(mWnd); - NS_ASSERTION(!IMEContext.get() || newIMEContext.get() == IMEContext.get(), - "Unknown IMC had been associated"); - } -#endif - return ret && !IMEContext.get(); - } - - if (mOnDestroyCalled) { - // If OnDestroy() has been called, we shouldn't disassociate the default - // IMC at destroying the window. - return false; - } - - if (!IMEContext.get()) { - return false; // already disassociated - } - - BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, 0); - NS_ASSERTION(ret, "ImmAssociateContextEx failed to disassociate the IMC"); - return ret != FALSE; -} - #ifdef ACCESSIBILITY #ifdef DEBUG_WMGETOBJECT From 053ccfc3c4f885c917507c94ee2a3bcd5ab61689 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:06 +0900 Subject: [PATCH 32/66] Bug 840409 part.12 Implement widget::IMEHandler::InitInputContext() and widget::IMEHandler::SetInputContext() r=jimm --- widget/windows/WinIMEHandler.cpp | 95 +++++++++++++++++++++++++++----- widget/windows/WinIMEHandler.h | 16 +++++- widget/windows/nsTextStore.h | 7 ++- widget/windows/nsWindow.cpp | 45 ++------------- 4 files changed, 105 insertions(+), 58 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index d764a11c3dd8..e2d80c627550 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -168,20 +168,6 @@ IMEHandler::GetUpdatePreference() return nsIMEUpdatePreference(false, false); } -void -IMEHandler::SetOpenState(nsWindow* aWindow, bool aOpen) -{ -#ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { - nsTextStore::SetIMEOpenState(aOpen); - return; - } -#endif //NS_ENABLE_TSF - - nsIMEContext IMEContext(aWindow->GetWindowHandle()); - IMEContext.SetOpenState(aOpen); -} - // static bool IMEHandler::GetOpenState(nsWindow* aWindow) @@ -200,10 +186,91 @@ IMEHandler::GetOpenState(nsWindow* aWindow) void IMEHandler::OnDestroyWindow(nsWindow* aWindow) { + // We need to do nothing here for TSF. Just restore the default context + // if it's been disassociated. nsIMEContext IMEContext(aWindow->GetWindowHandle()); IMEContext.AssociateDefaultContext(); } +// static +void +IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext) +{ + // FYI: If there is no composition, this call will do nothing. + NotifyIME(aWindow, REQUEST_TO_COMMIT_COMPOSITION); + + bool enable = (aInputContext.mIMEState.mEnabled == IMEState::ENABLED || + aInputContext.mIMEState.mEnabled == IMEState::PLUGIN); + bool adjustOpenState = (enable && + aInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE); + bool open = (adjustOpenState && + aInputContext.mIMEState.mOpen == IMEState::OPEN); + + aInputContext.mNativeIMEContext = nullptr; + +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + aInputContext.mNativeIMEContext = nsTextStore::GetTextStore(); + nsTextStore::SetInputContext(aInputContext); + // Currently, nsTextStore doesn't set focus to keyboard disabled document. + // Therefore, we still need to perform the following legacy code. + } +#endif // #ifdef NS_ENABLE_TSF + + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + if (enable) { + IMEContext.AssociateDefaultContext(); + if (!aInputContext.mNativeIMEContext) { + aInputContext.mNativeIMEContext = static_cast(IMEContext.get()); + } + } else if (!aWindow->Destroyed()) { + // Don't disassociate the context after the window is destroyed. + IMEContext.Disassociate(); + if (!aInputContext.mNativeIMEContext) { + // The old InputContext must store the default IMC. + aInputContext.mNativeIMEContext = + aWindow->GetInputContext().mNativeIMEContext; + } + } + + if (adjustOpenState) { +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + nsTextStore::SetIMEOpenState(open); + return; + } +#endif // #ifdef NS_ENABLE_TSF + IMEContext.SetOpenState(open); + } +} + +// static +void +IMEHandler::InitInputContext(nsWindow* aWindow, InputContext& aInputContext) +{ + // For a11y, the default enabled state should be 'enabled'. + aInputContext.mIMEState.mEnabled = IMEState::ENABLED; + +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + nsTextStore::SetInputContext(aInputContext); + aInputContext.mNativeIMEContext = nsTextStore::GetTextStore(); + MOZ_ASSERT(aInputContext.mNativeIMEContext); + return; + } +#endif // #ifdef NS_ENABLE_TSF + + // NOTE: mNativeIMEContext may be null if IMM module isn't installed. + nsIMEContext IMEContext(aWindow->GetWindowHandle()); + aInputContext.mNativeIMEContext = static_cast(IMEContext.get()); + MOZ_ASSERT(aInputContext.mNativeIMEContext || !CurrentKeyboardLayoutHasIME()); + // If no IME context is available, we should set the widget's pointer since + // nullptr indicates there is only one context per process on the platform. + if (!aInputContext.mNativeIMEContext) { + aInputContext.mNativeIMEContext = static_cast(aWindow); + } +} + #ifdef DEBUG // static bool diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index aacf7cdf24e1..fdd5af14777c 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -16,6 +16,8 @@ struct nsIMEUpdatePreference; namespace mozilla { namespace widget { +struct InputContext; + /** * IMEHandler class is a mediator class. On Windows, there are two IME API * sets: One is IMM which is legacy API set. The other is TSF which is modern @@ -68,9 +70,8 @@ public: static nsIMEUpdatePreference GetUpdatePreference(); /** - * Sets and Gets IME open state. + * Returns IME open state on the window. */ - static void SetOpenState(nsWindow* aWindow, bool aOpen); static bool GetOpenState(nsWindow* aWindow); /** @@ -78,6 +79,17 @@ public: */ static void OnDestroyWindow(nsWindow* aWindow); + /** + * Called when nsIWidget::SetInputContext() is called before the window's + * InputContext is modified actually. + */ + static void SetInputContext(nsWindow* aWindow, InputContext& aInputContext); + + /** + * Called when the window is created. + */ + static void InitInputContext(nsWindow* aWindow, InputContext& aInputContext); + /** * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index f677e3294758..ae1e510457a1 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -107,7 +107,7 @@ public: static void SetInputContext(const InputContext& aContext) { - if (!sTsfTextStore) return; + NS_ENSURE_TRUE_VOID(sTsfTextStore); sTsfTextStore->SetInputScope(aContext.mHTMLInputType); sTsfTextStore->SetInputContextInternal(aContext.mIMEState.mEnabled); } @@ -163,6 +163,11 @@ public: return (void*) & sDisplayAttrMgr; } + static void* GetTextStore() + { + return static_cast(sTsfTextStore); + } + static bool IsInTSFMode() { return sTsfThreadMgr != nullptr; diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 90aeee137849..3fc47f4826ec 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -589,16 +589,7 @@ nsWindow::Create(nsIWidget *aParent, SubclassWindow(TRUE); - // NOTE: mNativeIMEContext may be null if IMM module isn't installed. - nsIMEContext IMEContext(mWnd); - mInputContext.mNativeIMEContext = static_cast(IMEContext.get()); - MOZ_ASSERT(mInputContext.mNativeIMEContext || - !IMEHandler::CurrentKeyboardLayoutHasIME()); - // If no IME context is available, we should set this widget's pointer since - // nullptr indicates there is only one context per process on the platform. - if (!mInputContext.mNativeIMEContext) { - mInputContext.mNativeIMEContext = this; - } + IMEHandler::InitInputContext(this, mInputContext); // If the internal variable set by the config.trim_on_minimize pref has not // been initialized, and if this is the hidden window (conveniently created @@ -7390,37 +7381,9 @@ NS_IMETHODIMP_(void) nsWindow::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { -#ifdef NS_ENABLE_TSF - nsTextStore::SetInputContext(aContext); -#endif //NS_ENABLE_TSF - if (IMEHandler::IsComposing()) { - ResetInputState(); - } - void* nativeIMEContext = mInputContext.mNativeIMEContext; - mInputContext = aContext; - mInputContext.mNativeIMEContext = nullptr; - bool enable = (mInputContext.mIMEState.mEnabled == IMEState::ENABLED || - mInputContext.mIMEState.mEnabled == IMEState::PLUGIN); - - nsIMEContext IMEContext(mWnd); - if (enable) { - IMEContext.AssociateDefaultContext(); - mInputContext.mNativeIMEContext = static_cast(IMEContext.get()); - } else if (!mOnDestroyCalled) { - // Don't disassociate the context after the window is destroyed. - IMEContext.Disassociate(); - } - - // Restore the latest associated context when we cannot get actual context. - if (!mInputContext.mNativeIMEContext) { - mInputContext.mNativeIMEContext = nativeIMEContext; - } - - if (enable && - mInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE) { - bool open = (mInputContext.mIMEState.mOpen == IMEState::OPEN); - IMEHandler::SetOpenState(this, open); - } + InputContext newInputContext = aContext; + IMEHandler::SetInputContext(this, newInputContext); + mInputContext = newInputContext; } NS_IMETHODIMP_(InputContext) From bc51162884890ed654808cf063710d2d5c6b06f8 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:07 +0900 Subject: [PATCH 33/66] Bug 840409 part.13 Implement widget::IMEHandler::IsIMEEnabled() which checks whether the state indicates IME available or not r=jimm --- widget/windows/WinIMEHandler.cpp | 18 ++++++++++++++++-- widget/windows/WinIMEHandler.h | 10 +++++++--- widget/windows/nsWindow.cpp | 17 ++++++----------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index e2d80c627550..9651c8b8da68 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -49,6 +49,21 @@ IMEHandler::Terminate() nsIMM32Handler::Terminate(); } +// static +bool +IMEHandler::IsIMEEnabled(const InputContext& aInputContext) +{ + return IsIMEEnabled(aInputContext.mIMEState.mEnabled); +} + +// static +bool +IMEHandler::IsIMEEnabled(IMEState::Enabled aIMEState) +{ + return (aIMEState == mozilla::widget::IMEState::ENABLED || + aIMEState == mozilla::widget::IMEState::PLUGIN); +} + // static bool IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, @@ -199,8 +214,7 @@ IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext) // FYI: If there is no composition, this call will do nothing. NotifyIME(aWindow, REQUEST_TO_COMMIT_COMPOSITION); - bool enable = (aInputContext.mIMEState.mEnabled == IMEState::ENABLED || - aInputContext.mIMEState.mEnabled == IMEState::PLUGIN); + bool enable = IsIMEEnabled(aInputContext); bool adjustOpenState = (enable && aInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE); bool open = (adjustOpenState && diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index fdd5af14777c..9a554868211d 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -8,16 +8,14 @@ #include "nscore.h" #include "nsEvent.h" +#include "nsIWidget.h" #include class nsWindow; -struct nsIMEUpdatePreference; namespace mozilla { namespace widget { -struct InputContext; - /** * IMEHandler class is a mediator class. On Windows, there are two IME API * sets: One is IMM which is legacy API set. The other is TSF which is modern @@ -30,6 +28,12 @@ public: static void Initialize(); static void Terminate(); + /** + * Returns true if the context or IME state is enabled. Otherwise, false. + */ + static bool IsIMEEnabled(const InputContext& aInputContext); + static bool IsIMEEnabled(IMEState::Enabled aIMEState); + /** * When the message is not needed to handle anymore by the caller, this * returns true. Otherwise, false. diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 3fc47f4826ec..4b4e6ce36bbc 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -168,7 +168,6 @@ #include "nsIContent.h" #include "mozilla/HangMonitor.h" -#include "nsIMM32Handler.h" #include "WinIMEHandler.h" using namespace mozilla::widget; @@ -6480,7 +6479,7 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, static bool sRedirectedKeyDownEventPreventedDefault = false; bool noDefault; if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) { - nsIMEContext IMEContext(mWnd); + bool isIMEEnabled = IMEHandler::IsIMEEnabled(mInputContext); nsKeyEvent keydownEvent(true, NS_KEY_DOWN, this); keydownEvent.keyCode = DOMKeyCode; InitKeyEvent(keydownEvent, nativeKey, aModKeyState); @@ -6498,9 +6497,8 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, // application, we shouldn't redirect the message to it because the keydown // message is processed by us, so, nobody shouldn't process it. HWND focusedWnd = ::GetFocus(); - nsIMEContext newIMEContext(mWnd); if (!noDefault && !aFakeCharMessage && focusedWnd && !PluginHasFocus() && - !IMEContext.get() && newIMEContext.get()) { + !isIMEEnabled && IMEHandler::IsIMEEnabled(mInputContext)) { RemoveNextCharMessage(focusedWnd); INPUT keyinput; @@ -7390,13 +7388,10 @@ NS_IMETHODIMP_(InputContext) nsWindow::GetInputContext() { mInputContext.mIMEState.mOpen = IMEState::CLOSED; - switch (mInputContext.mIMEState.mEnabled) { - case IMEState::ENABLED: - case IMEState::PLUGIN: - if (IMEHandler::GetOpenState(this)) { - mInputContext.mIMEState.mOpen = IMEState::OPEN; - } - break; + if (IMEHandler::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) { + mInputContext.mIMEState.mOpen = IMEState::OPEN; + } else { + mInputContext.mIMEState.mOpen = IMEState::CLOSED; } return mInputContext; } From 9683bb0a0ef56a9b12fc73cb2fa3f495ff26f4be Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:07 +0900 Subject: [PATCH 34/66] Bug 840409 part.14 Implement widget::IMEHandler::GetNativeData() r=jimm --- widget/windows/WinIMEHandler.cpp | 20 ++++++++++++++++++++ widget/windows/WinIMEHandler.h | 5 +++++ widget/windows/nsTextStore.h | 25 ++++++++++++------------- widget/windows/nsWindow.cpp | 10 +--------- widget/windows/winrt/MetroWidget.cpp | 4 +--- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 9651c8b8da68..0171931a0c49 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -49,6 +49,26 @@ IMEHandler::Terminate() nsIMM32Handler::Terminate(); } +// static +void* +IMEHandler::GetNativeData(uint32_t aDataType) +{ +#ifdef NS_ENABLE_TSF + void* result = nsTextStore::GetNativeData(aDataType); + if (!result || !(*(static_cast(result)))) { + return nullptr; + } + // XXX During the TSF module test, sIsInTSFMode must be true. After that, + // the value should be restored but currently, there is no way for that. + // When the TSF test is enabled again, we need to fix this. Perhaps, + // sending a message can fix this. + sIsInTSFMode = true; + return result; +#else // #ifdef NS_ENABLE_TSF + return nullptr; +#endif // #ifdef NS_ENABLE_TSF #else +} + // static bool IMEHandler::IsIMEEnabled(const InputContext& aInputContext) diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 9a554868211d..721e4d45c44d 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -28,6 +28,11 @@ public: static void Initialize(); static void Terminate(); + /** + * Returns TSF related native data. + */ + static void* GetNativeData(uint32_t aDataType); + /** * Returns true if the context or IME state is enabled. Otherwise, false. */ diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index ae1e510457a1..d936a23aeba3 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -147,20 +147,19 @@ public: // Returns the address of the pointer so that the TSF automatic test can // replace the system object with a custom implementation for testing. - static void* GetThreadMgr(void) + static void* GetNativeData(uint32_t aDataType) { - Initialize(); // Apply any previous changes - return (void*) & sTsfThreadMgr; - } - - static void* GetCategoryMgr(void) - { - return (void*) & sCategoryMgr; - } - - static void* GetDisplayAttrMgr(void) - { - return (void*) & sDisplayAttrMgr; + switch (aDataType) { + case NS_NATIVE_TSF_THREAD_MGR: + Initialize(); // Apply any previous changes + return static_cast(&sTsfThreadMgr); + case NS_NATIVE_TSF_CATEGORY_MGR: + return static_cast(&sCategoryMgr); + case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: + return static_cast(&sDisplayAttrMgr); + default: + return nullptr; + } } static void* GetTextStore() diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 4b4e6ce36bbc..8bdd0e70836a 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -153,10 +153,6 @@ #include "nsIWinTaskbar.h" #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" -#if defined(NS_ENABLE_TSF) -#include "nsTextStore.h" -#endif // defined(NS_ENABLE_TSF) - // Windowless plugin support #include "npapi.h" @@ -2850,14 +2846,10 @@ void* nsWindow::GetNativeData(uint32_t aDataType) return (void*)::GetDC(mWnd); #endif -#ifdef NS_ENABLE_TSF case NS_NATIVE_TSF_THREAD_MGR: - return nsTextStore::GetThreadMgr(); case NS_NATIVE_TSF_CATEGORY_MGR: - return nsTextStore::GetCategoryMgr(); case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: - return nsTextStore::GetDisplayAttrMgr(); -#endif //NS_ENABLE_TSF + return IMEHandler::GetNativeData(aDataType); default: break; diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index 0b39860d42a2..a4654b4a2e66 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -1025,11 +1025,9 @@ MetroWidget::GetNativeData(uint32_t aDataType) } break; case NS_NATIVE_TSF_THREAD_MGR: - return nsTextStore::GetThreadMgr(); case NS_NATIVE_TSF_CATEGORY_MGR: - return nsTextStore::GetCategoryMgr(); case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: - return nsTextStore::GetDisplayAttrMgr(); + return nsTextStore::GetNativeData(aDataType); } return nullptr; } From ffde20f09fba0b85d565a5a6a9272d88f7edfd96 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:07 +0900 Subject: [PATCH 35/66] Bug 840409 part.15 Implement widget::IMEHandler::CanOptimizeKeyAndIMEMessages() r=jimm --- widget/windows/WinIMEHandler.cpp | 13 +++++++++++++ widget/windows/WinIMEHandler.h | 9 +++++++++ widget/windows/nsAppShell.cpp | 4 ++-- widget/windows/nsIMM32Handler.cpp | 2 +- widget/windows/nsIMM32Handler.h | 5 +---- widget/windows/nsTextStore.h | 6 ++++++ 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 0171931a0c49..2071842af802 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -69,6 +69,19 @@ IMEHandler::GetNativeData(uint32_t aDataType) #endif // #ifdef NS_ENABLE_TSF #else } +// static +bool +IMEHandler::CanOptimizeKeyAndIMEMessages() +{ +#ifdef NS_ENABLE_TSF + if (sIsInTSFMode) { + return nsTextStore::CanOptimizeKeyAndIMEMessages(); + } +#endif // #ifdef NS_ENABLE_TSF + + return nsIMM32Handler::CanOptimizeKeyAndIMEMessages(); +} + // static bool IMEHandler::IsIMEEnabled(const InputContext& aInputContext) diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 721e4d45c44d..44673a0f5905 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -11,6 +11,9 @@ #include "nsIWidget.h" #include +#define NS_WM_IMEFIRST WM_IME_SETCONTEXT +#define NS_WM_IMELAST WM_IME_KEYUP + class nsWindow; namespace mozilla { @@ -33,6 +36,12 @@ public: */ static void* GetNativeData(uint32_t aDataType); + /** + * Returns true if our message loop can optimize the message order for + * a key message or an IME message. Otherwise, false. + */ + static bool CanOptimizeKeyAndIMEMessages(); + /** * Returns true if the context or IME state is enabled. Otherwise, false. */ diff --git a/widget/windows/nsAppShell.cpp b/widget/windows/nsAppShell.cpp index 7da1ec7d1449..2e7240713e97 100644 --- a/widget/windows/nsAppShell.cpp +++ b/widget/windows/nsAppShell.cpp @@ -11,7 +11,7 @@ #include "WinMouseScrollHandler.h" #include "nsWindowDefs.h" #include "nsString.h" -#include "nsIMM32Handler.h" +#include "WinIMEHandler.h" #include "mozilla/widget/AudioSession.h" #include "mozilla/HangMonitor.h" @@ -66,7 +66,7 @@ static bool PeekUIMessage(MSG* aMsg) pMsg = &imeMsg; } - if (pMsg && !nsIMM32Handler::CanOptimizeKeyAndIMEMessages(pMsg)) { + if (pMsg && !mozilla::widget::IMEHandler::CanOptimizeKeyAndIMEMessages()) { return false; } diff --git a/widget/windows/nsIMM32Handler.cpp b/widget/windows/nsIMM32Handler.cpp index ee9e16bbd572..45a5ad937d39 100644 --- a/widget/windows/nsIMM32Handler.cpp +++ b/widget/windows/nsIMM32Handler.cpp @@ -137,7 +137,7 @@ nsIMM32Handler::GetKeyboardCodePage() } /* static */ bool -nsIMM32Handler::CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage) +nsIMM32Handler::CanOptimizeKeyAndIMEMessages() { // If IME is opening right now, we shouldn't optimize the key and IME message // order because ATOK (Japanese IME of third party) has some problem with the diff --git a/widget/windows/nsIMM32Handler.h b/widget/windows/nsIMM32Handler.h index b0f09d46c9cc..6da53143a7d0 100644 --- a/widget/windows/nsIMM32Handler.h +++ b/widget/windows/nsIMM32Handler.h @@ -16,9 +16,6 @@ class nsIWidget; class nsWindow; struct nsIntRect; -#define NS_WM_IMEFIRST WM_IME_SETCONTEXT -#define NS_WM_IMELAST WM_IME_KEYUP - class nsIMEContext { public: @@ -125,7 +122,7 @@ public: return IsComposing() && IsComposingWindow(aWindow); } - static bool CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage); + static bool CanOptimizeKeyAndIMEMessages(); #ifdef DEBUG /** diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index d936a23aeba3..97535380af64 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -145,6 +145,12 @@ public: ts->OnCompositionTimer(); } + static bool CanOptimizeKeyAndIMEMessages() + { + // TODO: We need to implement this for ATOK. + return true; + } + // Returns the address of the pointer so that the TSF automatic test can // replace the system object with a custom implementation for testing. static void* GetNativeData(uint32_t aDataType) From 5f44ab0ead50aef1a2b6893e0bec29adfb7a8a0f Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Mon, 25 Feb 2013 13:00:07 +0900 Subject: [PATCH 36/66] Bug 840409 part.16 Use IMM even in TSF mode when plugin has focus r=jimm --- widget/windows/WinIMEHandler.cpp | 37 +++++++++++++++++++++++--------- widget/windows/WinIMEHandler.h | 3 +++ widget/windows/nsTextStore.h | 5 +++++ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/widget/windows/WinIMEHandler.cpp b/widget/windows/WinIMEHandler.cpp index 2071842af802..6e15d42612a4 100644 --- a/widget/windows/WinIMEHandler.cpp +++ b/widget/windows/WinIMEHandler.cpp @@ -21,6 +21,7 @@ namespace widget { #ifdef NS_ENABLE_TSF bool IMEHandler::sIsInTSFMode = false; +bool IMEHandler::sPluginHasFocus = false; #endif // #ifdef NS_ENABLE_TSF // static @@ -74,7 +75,7 @@ bool IMEHandler::CanOptimizeKeyAndIMEMessages() { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { return nsTextStore::CanOptimizeKeyAndIMEMessages(); } #endif // #ifdef NS_ENABLE_TSF @@ -104,7 +105,7 @@ IMEHandler::ProcessMessage(nsWindow* aWindow, UINT aMessage, LRESULT* aRetValue, bool& aEatMessage) { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { if (aMessage == WM_USER_TSF_TEXTCHANGE) { nsTextStore::OnTextChangeMsg(); aEatMessage = true; @@ -123,7 +124,7 @@ bool IMEHandler::IsComposing() { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { return nsTextStore::IsComposing(); } #endif // #ifdef NS_ENABLE_TSF @@ -136,7 +137,7 @@ bool IMEHandler::IsComposingOn(nsWindow* aWindow) { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { return nsTextStore::IsComposingOn(aWindow); } #endif // #ifdef NS_ENABLE_TSF @@ -150,7 +151,7 @@ IMEHandler::NotifyIME(nsWindow* aWindow, NotificationToIME aNotification) { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { switch (aNotification) { case NOTIFY_IME_OF_SELECTION_CHANGE: return nsTextStore::OnSelectionChange(); @@ -183,6 +184,16 @@ IMEHandler::NotifyIME(nsWindow* aWindow, case REQUEST_TO_CANCEL_COMPOSITION: nsIMM32Handler::CancelComposition(aWindow); return NS_OK; +#ifdef NS_ENABLE_TSF + case NOTIFY_IME_OF_BLUR: + // If a plugin gets focus while TSF has focus, we need to notify TSF of + // the blur. + if (nsTextStore::ThinksHavingFocus()) { + return nsTextStore::OnFocusChange(false, aWindow, + aWindow->GetInputContext().mIMEState.mEnabled); + } + return NS_ERROR_NOT_IMPLEMENTED; +#endif //NS_ENABLE_TSF default: return NS_ERROR_NOT_IMPLEMENTED; } @@ -195,7 +206,7 @@ IMEHandler::NotifyIMEOfTextChange(uint32_t aStart, uint32_t aNewEnd) { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd); } #endif //NS_ENABLE_TSF @@ -208,7 +219,7 @@ nsIMEUpdatePreference IMEHandler::GetUpdatePreference() { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { return nsTextStore::GetIMEUpdatePreference(); } #endif //NS_ENABLE_TSF @@ -221,7 +232,7 @@ bool IMEHandler::GetOpenState(nsWindow* aWindow) { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { return nsTextStore::GetIMEOpenState(); } #endif //NS_ENABLE_TSF @@ -247,6 +258,9 @@ IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext) // FYI: If there is no composition, this call will do nothing. NotifyIME(aWindow, REQUEST_TO_COMMIT_COMPOSITION); + // Assume that SetInputContext() is called only when aWindow has focus. + sPluginHasFocus = (aInputContext.mIMEState.mEnabled == IMEState::PLUGIN); + bool enable = IsIMEEnabled(aInputContext); bool adjustOpenState = (enable && aInputContext.mIMEState.mOpen != IMEState::DONT_CHANGE_OPEN_STATE); @@ -256,9 +270,12 @@ IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext) aInputContext.mNativeIMEContext = nullptr; #ifdef NS_ENABLE_TSF + // Note that even while a plugin has focus, we need to notify TSF of that. if (sIsInTSFMode) { - aInputContext.mNativeIMEContext = nsTextStore::GetTextStore(); nsTextStore::SetInputContext(aInputContext); + if (IsTSFAvailable()) { + aInputContext.mNativeIMEContext = nsTextStore::GetTextStore(); + } // Currently, nsTextStore doesn't set focus to keyboard disabled document. // Therefore, we still need to perform the following legacy code. } @@ -282,7 +299,7 @@ IMEHandler::SetInputContext(nsWindow* aWindow, InputContext& aInputContext) if (adjustOpenState) { #ifdef NS_ENABLE_TSF - if (sIsInTSFMode) { + if (IsTSFAvailable()) { nsTextStore::SetIMEOpenState(open); return; } diff --git a/widget/windows/WinIMEHandler.h b/widget/windows/WinIMEHandler.h index 44673a0f5905..b9b219e43d47 100644 --- a/widget/windows/WinIMEHandler.h +++ b/widget/windows/WinIMEHandler.h @@ -125,6 +125,9 @@ public: private: #ifdef NS_ENABLE_TSF static bool sIsInTSFMode; + static bool sPluginHasFocus; + + static bool IsTSFAvailable() { return (sIsInTSFMode && !sPluginHasFocus); } #endif // #ifdef NS_ENABLE_TSF }; diff --git a/widget/windows/nsTextStore.h b/widget/windows/nsTextStore.h index 97535380af64..7ba6633d5572 100644 --- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -173,6 +173,11 @@ public: return static_cast(sTsfTextStore); } + static bool ThinksHavingFocus() + { + return (sTsfTextStore && sTsfTextStore->mContext); + } + static bool IsInTSFMode() { return sTsfThreadMgr != nullptr; From c43fe3d9b76383be8a10316dcfd494eed7b3296c Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Sun, 24 Feb 2013 20:15:23 -0800 Subject: [PATCH 37/66] Bug 843821 - Support reading distribution resources from a /system location. r=mfinkle --- mobile/android/base/Distribution.java | 24 ++++++++++++++++++++++-- mobile/android/chrome/content/browser.js | 22 +++++++++++++++++----- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/mobile/android/base/Distribution.java b/mobile/android/base/Distribution.java index 6c7d94a1b3e6..ebda15df5933 100644 --- a/mobile/android/base/Distribution.java +++ b/mobile/android/base/Distribution.java @@ -56,21 +56,41 @@ public final class Distribution { return; } + // This pref stores the path to the distribution directory. If it is null, Gecko + // looks for distribution files in /data/data/org.mozilla.xxx/distribution. + String pathKeyName = context.getPackageName() + ".distribution_path"; + String distPath = null; + // Send a message to Gecko if we've set a distribution. if (state == STATE_SET) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", null)); + distPath = settings.getString(pathKeyName, null); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", distPath)); return; } boolean distributionSet = false; try { + // First, try copying distribution files out of the APK. distributionSet = copyFiles(context, packagePath); } catch (IOException e) { Log.e(LOGTAG, "Error copying distribution files", e); } + if (!distributionSet) { + // If there aren't any distribution files in the APK, look in the /system directory. + File distDir = new File("/system/" + context.getPackageName() + "/distribution"); + if (distDir.exists()) { + distributionSet = true; + distPath = distDir.getPath(); + settings.edit().putString(pathKeyName, distPath).commit(); + } + } + + Log.i("BOOM", "distributionSet: " + distributionSet); + Log.i("BOOM", "distPath: " + distPath); + if (distributionSet) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", null)); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", distPath)); settings.edit().putInt(keyName, STATE_SET).commit(); } else { settings.edit().putInt(keyName, STATE_NONE).commit(); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 406b90a1531d..95174800e134 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -8152,15 +8152,19 @@ var MemoryObserver = { }; var Distribution = { + // File used to store campaign data _file: null, + // Path to distribution directory for distribution customizations + _path: null, + init: function dc_init() { Services.obs.addObserver(this, "Distribution:Set", false); Services.obs.addObserver(this, "prefservice:after-app-defaults", false); Services.obs.addObserver(this, "Campaign:Set", false); // Look for file outside the APK: - // /data/data/org.mozilla.fennec/distribution.json + // /data/data/org.mozilla.xxx/distribution.json this._file = Services.dirsvc.get("XCurProcD", Ci.nsIFile); this._file.append("distribution.json"); this.readJSON(this._file, this.update); @@ -8175,6 +8179,8 @@ var Distribution = { observe: function dc_observe(aSubject, aTopic, aData) { switch (aTopic) { case "Distribution:Set": + this._path = aData; + // Reload the default prefs so we can observe "prefservice:after-app-defaults" Services.prefs.QueryInterface(Ci.nsIObserver).observe(null, "reload-default-prefs", null); break; @@ -8215,10 +8221,16 @@ var Distribution = { }, getPrefs: function dc_getPrefs() { - // Look for preferences file outside the APK: - // /data/data/org.mozilla.fennec/distribution/preferences.json - let file = Services.dirsvc.get("XCurProcD", Ci.nsIFile); - file.append("distribution"); + let file; + if (this._path) { + file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + file.initWithPath(this._path); + } else { + // If a path isn't specified, look in the data directory: + // /data/data/org.mozilla.xxx/distribution + file = Services.dirsvc.get("XCurProcD", Ci.nsIFile); + file.append("distribution"); + } file.append("preferences.json"); this.readJSON(file, this.applyPrefs); From ee53812c345232d3750f8ee9edede66676e1a751 Mon Sep 17 00:00:00 2001 From: Gina Yeh Date: Mon, 25 Feb 2013 12:40:31 +0800 Subject: [PATCH 38/66] Bug 830551 - Send file-transfer-complete message after receiving unexpected response code from remote, r=echou --- dom/bluetooth/BluetoothOppManager.cpp | 38 ++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/dom/bluetooth/BluetoothOppManager.cpp b/dom/bluetooth/BluetoothOppManager.cpp index d210451851c8..df0caeba5d9b 100644 --- a/dom/bluetooth/BluetoothOppManager.cpp +++ b/dom/bluetooth/BluetoothOppManager.cpp @@ -361,6 +361,7 @@ BluetoothOppManager::SendFile(BlobParent* aActor) SendConnectRequest(); mTransferMode = false; + StartFileTransfer(); return true; } @@ -797,24 +798,26 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage) packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2]; } - // Check response code - if (mLastCommand == ObexRequestCode::Put && - opCode != ObexResponseCode::Continue) { - NS_WARNING("[OPP] Put(0x02) failed"); - SendDisconnectRequest(); - return; - } else if (mLastCommand == ObexRequestCode::Abort || - mLastCommand == ObexRequestCode::Connect || - mLastCommand == ObexRequestCode::Disconnect || - mLastCommand == ObexRequestCode::PutFinal){ - if (opCode != ObexResponseCode::Success) { - nsAutoCString str; - str += "[OPP] 0x"; - str += mLastCommand; - str += " failed"; - NS_WARNING(str.get()); - return; + // Check response code and send out system message as finished if the reponse + // code is somehow incorrect. + uint8_t expectedOpCode = ObexResponseCode::Success; + if (mLastCommand == ObexRequestCode::Put) { + expectedOpCode = ObexResponseCode::Continue; + } + + if (opCode != expectedOpCode) { + if (mLastCommand == ObexRequestCode::Put || + mLastCommand == ObexRequestCode::Abort || + mLastCommand == ObexRequestCode::PutFinal) { + SendDisconnectRequest(); } + nsAutoCString str; + str += "[OPP] 0x"; + str.AppendInt(mLastCommand, 16); + str += " failed"; + NS_WARNING(str.get()); + FileTransferComplete(); + return; } if (mLastCommand == ObexRequestCode::PutFinal) { @@ -849,7 +852,6 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage) */ if (ExtractBlobHeaders()) { sInstance->SendPutHeaderRequest(sFileName, sFileLength); - StartFileTransfer(); } } else if (mLastCommand == ObexRequestCode::Put) { From be16192d59a7af66c47b87b23dcc80be050d7003 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Sun, 24 Feb 2013 20:51:04 -0800 Subject: [PATCH 39/66] Bug 842883 - (Part 1) Start moving some tab-specific event listeners to Tabs. r=bnicholson --- mobile/android/base/BrowserApp.java | 14 +++----- mobile/android/base/GeckoApp.java | 44 ------------------------ mobile/android/base/Tabs.java | 20 ++++++++++- mobile/android/chrome/content/browser.js | 4 +++ 4 files changed, 27 insertions(+), 55 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 20da97ca0587..c6878867075b 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -123,6 +123,9 @@ abstract public class BrowserApp extends GeckoApp invalidateOptionsMenu(); } break; + case LINK_ADDED: + handleLinkAdded(tab); + break; } super.onTabChanged(tab, msg, data); } @@ -141,16 +144,7 @@ abstract public class BrowserApp extends GeckoApp }); } - @Override - void handleLinkAdded(final int tabId, String rel, final String href, int size) { - super.handleLinkAdded(tabId, rel, href, size); - if (rel.indexOf("[icon]") == -1) - return; - - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - + void handleLinkAdded(final Tab tab) { // If tab is not loading and the favicon is updated, we // want to load the image straight away. If tab is still // loading, we only load the favicon once the page's content diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 7249416d3c0b..9fd6d6cab62b 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -776,19 +776,6 @@ abstract public class GeckoApp if (layerView != null && Tabs.getInstance().isSelectedTab(tab)) { layerView.setBackgroundColor(tab.getBackgroundColor()); } - } else if (event.equals("DOMTitleChanged")) { - final int tabId = message.getInt("tabID"); - final String title = message.getString("title"); - handleTitleChanged(tabId, title); - } else if (event.equals("DOMLinkAdded")) { - final int tabId = message.getInt("tabID"); - final String rel = message.getString("rel"); - final String href = message.getString("href"); - final int size = message.getInt("size"); - handleLinkAdded(tabId, rel, href, size); - } else if (event.equals("DOMWindowClose")) { - final int tabId = message.getInt("tabID"); - handleWindowClose(tabId); } else if (event.equals("log")) { // generic log listener final String msg = message.getString("msg"); @@ -1102,31 +1089,6 @@ abstract public class GeckoApp Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOADED); } - void handleTitleChanged(int tabId, String title) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - tab.updateTitle(title); - } - - void handleLinkAdded(final int tabId, String rel, final String href, int size) { - if (rel.indexOf("[icon]") == -1) - return; - - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - tab.updateFaviconURL(href, size); - } - - void handleWindowClose(final int tabId) { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getTab(tabId); - tabs.closeTab(tab); - } - private void addFullScreenPluginView(View view) { if (mFullScreenPluginView != null) { Log.w(LOGTAG, "Already have a fullscreen plugin view"); @@ -1766,9 +1728,6 @@ abstract public class GeckoApp //register for events registerEventListener("DOMContentLoaded"); - registerEventListener("DOMTitleChanged"); - registerEventListener("DOMLinkAdded"); - registerEventListener("DOMWindowClose"); registerEventListener("log"); registerEventListener("Content:SecurityChange"); registerEventListener("Content:ReaderEnabled"); @@ -2167,9 +2126,6 @@ abstract public class GeckoApp GeckoAppShell.sendEventToGecko(GeckoEvent.createShutdownEvent()); unregisterEventListener("DOMContentLoaded"); - unregisterEventListener("DOMTitleChanged"); - unregisterEventListener("DOMLinkAdded"); - unregisterEventListener("DOMWindowClose"); unregisterEventListener("log"); unregisterEventListener("Content:SecurityChange"); unregisterEventListener("Content:ReaderEnabled"); diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index b20211b1fa7b..8e4499b0945e 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -72,6 +72,9 @@ public class Tabs implements GeckoEventListener { registerEventListener("Reader:Added"); registerEventListener("Reader:Removed"); registerEventListener("Reader:Share"); + registerEventListener("DOMTitleChanged"); + registerEventListener("DOMLinkAdded"); + registerEventListener("DOMWindowClose"); } public void attachToActivity(GeckoApp activity) { @@ -333,6 +336,20 @@ public class Tabs implements GeckoEventListener { GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title); + } else if (event.equals("DOMTitleChanged")) { + Tab tab = getTab(message.getInt("tabID")); + if (tab != null) { + tab.updateTitle(message.getString("title")); + } + } else if (event.equals("DOMLinkAdded")) { + Tab tab = getTab(message.getInt("tabID")); + if (tab != null) { + tab.updateFaviconURL(message.getString("href"), message.getInt("size")); + notifyListeners(tab, TabEvents.LINK_ADDED); + } + } else if (event.equals("DOMWindowClose")) { + Tab tab = getTab(message.getInt("tabID")); + closeTab(tab); } } catch (Exception e) { Log.w(LOGTAG, "handleMessage threw for " + event, e); @@ -409,7 +426,8 @@ public class Tabs implements GeckoEventListener { ADDED, RESTORED, LOCATION_CHANGE, - MENU_UPDATED + MENU_UPDATED, + LINK_ADDED } public void notifyListeners(Tab tab, TabEvents msg) { diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 95174800e134..30d884569d5f 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3450,6 +3450,10 @@ Tab.prototype = { list.push("[" + rel + "]"); } + // We only care about icon links + if (list.indexOf("[icon]") == -1) + return; + // We want to get the largest icon size possible for our UI. let maxSize = 0; From dbb131c728fe905a3e72e85f410616be232aa0a9 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Sun, 24 Feb 2013 20:51:04 -0800 Subject: [PATCH 40/66] Bug 842883 - (Part 2) Move the Reader:* event handlers out of Tabs that don't need to be there. r=bnicholson --- mobile/android/base/BrowserApp.java | 36 ++++++++++++++++++++++++++ mobile/android/base/GeckoApp.java | 6 +++++ mobile/android/base/Tabs.java | 40 ----------------------------- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index c6878867075b..0d7f37375772 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -179,6 +179,29 @@ abstract public class BrowserApp extends GeckoApp }); } + void handleReaderAdded(boolean success, final String title, final String url) { + if (!success) { + showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT); + return; + } + + GeckoAppShell.getHandler().post(new Runnable() { + public void run() { + BrowserDB.addReadingListItem(getContentResolver(), title, url); + showToast(R.string.reading_list_added, Toast.LENGTH_SHORT); + } + }); + } + + void handleReaderRemoved(final String url) { + GeckoAppShell.getHandler().post(new Runnable() { + public void run() { + BrowserDB.removeReadingListItemWithURL(getContentResolver(), url); + showToast(R.string.reading_list_removed, Toast.LENGTH_SHORT); + } + }); + } + void handleReaderEnabled(final int tabId) { super.handleReaderEnabled(tabId); final Tab tab = Tabs.getInstance().getTab(tabId); @@ -530,6 +553,19 @@ abstract public class BrowserApp extends GeckoApp Telemetry.HistogramAdd("PLACES_BOOKMARKS_COUNT", BrowserDB.getCount(getContentResolver(), "bookmarks")); Telemetry.HistogramAdd("FENNEC_FAVICONS_COUNT", BrowserDB.getCount(getContentResolver(), "favicons")); Telemetry.HistogramAdd("FENNEC_THUMBNAILS_COUNT", BrowserDB.getCount(getContentResolver(), "thumbnails")); + } else if (event.equals("Reader:Added")) { + final boolean success = message.getBoolean("success"); + final String title = message.getString("title"); + final String url = message.getString("url"); + handleReaderAdded(success, title, url); + } else if (event.equals("Reader:Removed")) { + final String url = message.getString("url"); + handleReaderRemoved(url); + } else if (event.equals("Reader:Share")) { + final String title = message.getString("title"); + final String url = message.getString("url"); + GeckoAppShell.openUriExternal(url, "text/plain", "", "", + Intent.ACTION_SEND, title); } else { super.handleMessage(event, message); } diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 9fd6d6cab62b..93374f5220c4 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1734,6 +1734,9 @@ abstract public class GeckoApp registerEventListener("Content:StateChange"); registerEventListener("Content:LoadError"); registerEventListener("Content:PageShow"); + registerEventListener("Reader:Added"); + registerEventListener("Reader:Removed"); + registerEventListener("Reader:Share"); registerEventListener("Reader:FaviconRequest"); registerEventListener("Reader:GoToReadingList"); registerEventListener("onCameraCapture"); @@ -2132,6 +2135,9 @@ abstract public class GeckoApp unregisterEventListener("Content:StateChange"); unregisterEventListener("Content:LoadError"); unregisterEventListener("Content:PageShow"); + unregisterEventListener("Reader:Added"); + unregisterEventListener("Reader:Removed"); + unregisterEventListener("Reader:Share"); unregisterEventListener("Reader:FaviconRequest"); unregisterEventListener("Reader:GoToReadingList"); unregisterEventListener("onCameraCapture"); diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 8e4499b0945e..10a52ad645a4 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -69,9 +69,6 @@ public class Tabs implements GeckoEventListener { registerEventListener("Tab:Select"); registerEventListener("Content:LocationChange"); registerEventListener("Session:RestoreEnd"); - registerEventListener("Reader:Added"); - registerEventListener("Reader:Removed"); - registerEventListener("Reader:Share"); registerEventListener("DOMTitleChanged"); registerEventListener("DOMLinkAdded"); registerEventListener("DOMWindowClose"); @@ -322,20 +319,6 @@ public class Tabs implements GeckoEventListener { } } else if (event.equals("Session:RestoreEnd")) { notifyListeners(null, TabEvents.RESTORED); - } else if (event.equals("Reader:Added")) { - final boolean success = message.getBoolean("success"); - final String title = message.getString("title"); - final String url = message.getString("url"); - handleReaderAdded(success, title, url); - } else if (event.equals("Reader:Removed")) { - final String url = message.getString("url"); - handleReaderRemoved(url); - } else if (event.equals("Reader:Share")) { - final String title = message.getString("title"); - final String url = message.getString("url"); - - GeckoAppShell.openUriExternal(url, "text/plain", "", "", - Intent.ACTION_SEND, title); } else if (event.equals("DOMTitleChanged")) { Tab tab = getTab(message.getInt("tabID")); if (tab != null) { @@ -356,29 +339,6 @@ public class Tabs implements GeckoEventListener { } } - void handleReaderAdded(boolean success, final String title, final String url) { - if (!success) { - mActivity.showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT); - return; - } - - GeckoAppShell.getHandler().post(new Runnable() { - public void run() { - BrowserDB.addReadingListItem(getContentResolver(), title, url); - mActivity.showToast(R.string.reading_list_added, Toast.LENGTH_SHORT); - } - }); - } - - void handleReaderRemoved(final String url) { - GeckoAppShell.getHandler().post(new Runnable() { - public void run() { - BrowserDB.removeReadingListItemWithURL(getContentResolver(), url); - mActivity.showToast(R.string.reading_list_removed, Toast.LENGTH_SHORT); - } - }); - } - public void refreshThumbnails() { final ThumbnailHelper helper = ThumbnailHelper.getInstance(); Iterator iterator = mTabs.values().iterator(); From 00836fba523b4cedec76d70fd872d646f05eeb42 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Sun, 24 Feb 2013 20:51:05 -0800 Subject: [PATCH 41/66] Bug 842883 - (Part 2.5) Clean up Tabs.handleMessage to avoid calling getTab() all the time. r=bnicholson --- mobile/android/base/Tabs.java | 63 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 10a52ad645a4..c89536b1fc14 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -59,6 +59,7 @@ public class Tabs implements GeckoEventListener { private ContentObserver mContentObserver; private Tabs() { + registerEventListener("Session:RestoreEnd"); registerEventListener("SessionHistory:New"); registerEventListener("SessionHistory:Back"); registerEventListener("SessionHistory:Forward"); @@ -68,7 +69,6 @@ public class Tabs implements GeckoEventListener { registerEventListener("Tab:Close"); registerEventListener("Tab:Select"); registerEventListener("Content:LocationChange"); - registerEventListener("Session:RestoreEnd"); registerEventListener("DOMTitleChanged"); registerEventListener("DOMLinkAdded"); registerEventListener("DOMWindowClose"); @@ -275,25 +275,25 @@ public class Tabs implements GeckoEventListener { public void handleMessage(String event, JSONObject message) { try { - if (event.startsWith("SessionHistory:")) { - Tab tab = getTab(message.getInt("tabID")); - if (tab != null) { - event = event.substring("SessionHistory:".length()); - tab.handleSessionHistoryMessage(event, message); - } - } else if (event.equals("Tab:Added")) { + if (event.equals("Session:RestoreEnd")) { + notifyListeners(null, TabEvents.RESTORED); + return; + } + + // All other events handled below should contain a tabID property + int id = message.getInt("tabID"); + Tab tab = getTab(id); + + // "Tab:Added" is a special case because tab will be null if the tab was just added + if (event.equals("Tab:Added")) { String url = message.isNull("uri") ? null : message.getString("uri"); - int id = message.getInt("tabID"); - Tab tab = null; if (message.getBoolean("stub")) { - if (mTabs.containsKey(id)) { - tab = mTabs.get(id); - tab.updateURL(url); - } else { + if (tab == null) { // Tab was already closed; abort return; } + tab.updateURL(url); } else { tab = addTab(id, url, message.getBoolean("external"), message.getInt("parentId"), @@ -302,36 +302,33 @@ public class Tabs implements GeckoEventListener { } if (message.getBoolean("selected")) - selectTab(tab.getId()); + selectTab(id); if (message.getBoolean("delayLoad")) tab.setState(Tab.STATE_DELAYED); if (message.getBoolean("desktopMode")) tab.setDesktopMode(true); + return; + } + + // Tab was already closed; abort + if (tab == null) + return; + + if (event.startsWith("SessionHistory:")) { + event = event.substring("SessionHistory:".length()); + tab.handleSessionHistoryMessage(event, message); } else if (event.equals("Tab:Close")) { - Tab tab = getTab(message.getInt("tabID")); closeTab(tab); } else if (event.equals("Tab:Select")) { - selectTab(message.getInt("tabID")); + selectTab(tab.getId()); } else if (event.equals("Content:LocationChange")) { - Tab tab = getTab(message.getInt("tabID")); - if (tab != null) { - tab.handleLocationChange(message); - } - } else if (event.equals("Session:RestoreEnd")) { - notifyListeners(null, TabEvents.RESTORED); + tab.handleLocationChange(message); } else if (event.equals("DOMTitleChanged")) { - Tab tab = getTab(message.getInt("tabID")); - if (tab != null) { - tab.updateTitle(message.getString("title")); - } + tab.updateTitle(message.getString("title")); } else if (event.equals("DOMLinkAdded")) { - Tab tab = getTab(message.getInt("tabID")); - if (tab != null) { - tab.updateFaviconURL(message.getString("href"), message.getInt("size")); - notifyListeners(tab, TabEvents.LINK_ADDED); - } + tab.updateFaviconURL(message.getString("href"), message.getInt("size")); + notifyListeners(tab, TabEvents.LINK_ADDED); } else if (event.equals("DOMWindowClose")) { - Tab tab = getTab(message.getInt("tabID")); closeTab(tab); } } catch (Exception e) { From a318817e7d33c70124a540adde1bc5f1e680e330 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Sun, 24 Feb 2013 20:51:05 -0800 Subject: [PATCH 42/66] Bug 842883 - (Part 3) Move Content:* events from GeckoApp to Tabs. r=bnicholson --- mobile/android/base/BrowserApp.java | 32 +++---- mobile/android/base/GeckoApp.java | 105 ----------------------- mobile/android/base/Tab.java | 29 ++++++- mobile/android/base/Tabs.java | 32 ++++++- mobile/android/chrome/content/browser.js | 8 +- 5 files changed, 73 insertions(+), 133 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 0d7f37375772..4f654055031d 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -123,20 +123,23 @@ abstract public class BrowserApp extends GeckoApp invalidateOptionsMenu(); } break; + case PAGE_SHOW: + handlePageShow(tab); + break; case LINK_ADDED: handleLinkAdded(tab); break; + case SECURITY_CHANGE: + handleSecurityChange(tab); + break; + case READER_ENABLED: + handleReaderEnabled(tab); + break; } super.onTabChanged(tab, msg, data); } - @Override - void handlePageShow(final int tabId) { - super.handlePageShow(tabId); - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - + void handlePageShow(final Tab tab) { mMainHandler.post(new Runnable() { public void run() { loadFavicon(tab); @@ -164,13 +167,7 @@ abstract public class BrowserApp extends GeckoApp updateAboutHomeTopSites(); } - @Override - void handleSecurityChange(final int tabId, final JSONObject identityData) { - super.handleSecurityChange(tabId, identityData); - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - + void handleSecurityChange(final Tab tab) { mMainHandler.post(new Runnable() { public void run() { if (Tabs.getInstance().isSelectedTab(tab)) @@ -202,12 +199,7 @@ abstract public class BrowserApp extends GeckoApp }); } - void handleReaderEnabled(final int tabId) { - super.handleReaderEnabled(tabId); - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - + void handleReaderEnabled(final Tab tab) { mMainHandler.post(new Runnable() { public void run() { if (Tabs.getInstance().isSelectedTab(tab)) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 93374f5220c4..f45bbb93213d 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -609,22 +609,6 @@ abstract public class GeckoApp outState.putString(SAVED_STATE_PRIVATE_SESSION, mPrivateBrowsingSession); } - void handleSecurityChange(final int tabId, final JSONObject identityData) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - tab.updateIdentityData(identityData); - } - - void handleReaderEnabled(final int tabId) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - tab.setReaderEnabled(true); - } - void handleFaviconRequest(final String url) { (new UiAsyncTask(mMainHandler, GeckoAppShell.getHandler()) { @Override @@ -650,17 +634,6 @@ abstract public class GeckoApp }).execute(); } - void handleLoadError(final int tabId, final String uri, final String title) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - // When a load error occurs, the URLBar can get corrupt so we reset it - Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR); - } - - void handlePageShow(final int tabId) { } - void handleClearHistory() { BrowserDB.clearHistory(getContentResolver()); } @@ -780,43 +753,11 @@ abstract public class GeckoApp // generic log listener final String msg = message.getString("msg"); Log.d(LOGTAG, "Log: " + msg); - } else if (event.equals("Content:SecurityChange")) { - final int tabId = message.getInt("tabID"); - final JSONObject identity = message.getJSONObject("identity"); - Log.i(LOGTAG, "Security Mode - " + identity.getString("mode")); - handleSecurityChange(tabId, identity); - } else if (event.equals("Content:ReaderEnabled")) { - final int tabId = message.getInt("tabID"); - handleReaderEnabled(tabId); } else if (event.equals("Reader:FaviconRequest")) { final String url = message.getString("url"); handleFaviconRequest(url); } else if (event.equals("Reader:GoToReadingList")) { showReadingList(); - } else if (event.equals("Content:StateChange")) { - final int tabId = message.getInt("tabID"); - final String uri = message.getString("uri"); - final boolean success = message.getBoolean("success"); - int state = message.getInt("state"); - Log.d(LOGTAG, "State - " + state); - if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) { - if ((state & GeckoAppShell.WPL_STATE_START) != 0) { - Log.d(LOGTAG, "Got a document start event."); - final boolean showProgress = message.getBoolean("showProgress"); - handleDocumentStart(tabId, showProgress, uri); - } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) { - Log.d(LOGTAG, "Got a document stop event."); - handleDocumentStop(tabId, success); - } - } - } else if (event.equals("Content:LoadError")) { - final int tabId = message.getInt("tabID"); - final String uri = message.getString("uri"); - final String title = message.getString("title"); - handleLoadError(tabId, uri, title); - } else if (event.equals("Content:PageShow")) { - final int tabId = message.getInt("tabID"); - handlePageShow(tabId); } else if (event.equals("Gecko:Ready")) { mGeckoReadyStartupTimer.stop(); connectGeckoLayerClient(); @@ -1028,38 +969,6 @@ abstract public class GeckoApp }); } - void handleDocumentStart(int tabId, final boolean showProgress, String uri) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - tab.setState(shouldShowProgress(uri) ? Tab.STATE_SUCCESS : Tab.STATE_LOADING); - tab.updateIdentityData(null); - tab.setReaderEnabled(false); - Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.START, showProgress); - } - - void handleDocumentStop(int tabId, boolean success) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - tab.setState(success ? Tab.STATE_SUCCESS : Tab.STATE_ERROR); - - Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.STOP); - - final String oldURL = tab.getURL(); - GeckoAppShell.getHandler().postDelayed(new Runnable() { - public void run() { - // tab.getURL() may return null - if (!TextUtils.equals(oldURL, tab.getURL())) - return; - - ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab); - } - }, 500); - } - public void showToast(final int resId, final int duration) { mMainHandler.post(new Runnable() { public void run() { @@ -1729,11 +1638,6 @@ abstract public class GeckoApp //register for events registerEventListener("DOMContentLoaded"); registerEventListener("log"); - registerEventListener("Content:SecurityChange"); - registerEventListener("Content:ReaderEnabled"); - registerEventListener("Content:StateChange"); - registerEventListener("Content:LoadError"); - registerEventListener("Content:PageShow"); registerEventListener("Reader:Added"); registerEventListener("Reader:Removed"); registerEventListener("Reader:Share"); @@ -2130,11 +2034,6 @@ abstract public class GeckoApp unregisterEventListener("DOMContentLoaded"); unregisterEventListener("log"); - unregisterEventListener("Content:SecurityChange"); - unregisterEventListener("Content:ReaderEnabled"); - unregisterEventListener("Content:StateChange"); - unregisterEventListener("Content:LoadError"); - unregisterEventListener("Content:PageShow"); unregisterEventListener("Reader:Added"); unregisterEventListener("Reader:Removed"); unregisterEventListener("Reader:Share"); @@ -2715,10 +2614,6 @@ abstract public class GeckoApp return false; } - public static boolean shouldShowProgress(String url) { - return "about:home".equals(url) || ReaderModeUtils.isAboutReader(url); - } - public static void assertOnUiThread() { Thread uiThread = mAppContext.getMainLooper().getThread(); assertOnThread(uiThread); diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 2ce534ccfa3d..6d15d1549a01 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -22,6 +22,7 @@ import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; +import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -88,7 +89,7 @@ public class Tab { mZoomConstraints = new ZoomConstraints(false); mPluginViews = new ArrayList(); mPluginLayers = new HashMap(); - mState = GeckoApp.shouldShowProgress(url) ? STATE_SUCCESS : STATE_LOADING; + mState = shouldShowProgress(url) ? STATE_SUCCESS : STATE_LOADING; } private ContentResolver getContentResolver() { @@ -529,6 +530,32 @@ public class Tab { Tabs.getInstance().notifyListeners(this, Tabs.TabEvents.LOCATION_CHANGE, uri); } + private boolean shouldShowProgress(String url) { + return "about:home".equals(url) || ReaderModeUtils.isAboutReader(url); + } + + void handleDocumentStart(boolean showProgress, String url) { + setState(shouldShowProgress(url) ? STATE_SUCCESS : STATE_LOADING); + updateIdentityData(null); + setReaderEnabled(false); + } + + void handleDocumentStop(boolean success) { + setState(success ? STATE_SUCCESS : STATE_ERROR); + + final String oldURL = getURL(); + final Tab tab = this; + GeckoAppShell.getHandler().postDelayed(new Runnable() { + public void run() { + // tab.getURL() may return null + if (!TextUtils.equals(oldURL, getURL())) + return; + + ThumbnailHelper.getInstance().getAndProcessThumbnailFor(tab); + } + }, 500); + } + protected void saveThumbnailToDB() { try { String url = getURL(); diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index c89536b1fc14..046d2cacc629 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -69,6 +69,11 @@ public class Tabs implements GeckoEventListener { registerEventListener("Tab:Close"); registerEventListener("Tab:Select"); registerEventListener("Content:LocationChange"); + registerEventListener("Content:SecurityChange"); + registerEventListener("Content:ReaderEnabled"); + registerEventListener("Content:StateChange"); + registerEventListener("Content:LoadError"); + registerEventListener("Content:PageShow"); registerEventListener("DOMTitleChanged"); registerEventListener("DOMLinkAdded"); registerEventListener("DOMWindowClose"); @@ -323,6 +328,28 @@ public class Tabs implements GeckoEventListener { selectTab(tab.getId()); } else if (event.equals("Content:LocationChange")) { tab.handleLocationChange(message); + } else if (event.equals("Content:SecurityChange")) { + tab.updateIdentityData(message.getJSONObject("identity")); + notifyListeners(tab, TabEvents.SECURITY_CHANGE); + } else if (event.equals("Content:ReaderEnabled")) { + tab.setReaderEnabled(true); + notifyListeners(tab, TabEvents.READER_ENABLED); + } else if (event.equals("Content:StateChange")) { + int state = message.getInt("state"); + if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) { + if ((state & GeckoAppShell.WPL_STATE_START) != 0) { + boolean showProgress = message.getBoolean("showProgress"); + tab.handleDocumentStart(showProgress, message.getString("uri")); + notifyListeners(tab, Tabs.TabEvents.START, showProgress); + } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) { + tab.handleDocumentStop(message.getBoolean("success")); + notifyListeners(tab, Tabs.TabEvents.STOP); + } + } + } else if (event.equals("Content:LoadError")) { + notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR); + } else if (event.equals("Content:PageShow")) { + notifyListeners(tab, TabEvents.PAGE_SHOW); } else if (event.equals("DOMTitleChanged")) { tab.updateTitle(message.getString("title")); } else if (event.equals("DOMLinkAdded")) { @@ -384,7 +411,10 @@ public class Tabs implements GeckoEventListener { RESTORED, LOCATION_CHANGE, MENU_UPDATED, - LINK_ADDED + PAGE_SHOW, + LINK_ADDED, + SECURITY_CHANGE, + READER_ENABLED } public void notifyListeners(Tab tab, TabEvents msg) { diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 30d884569d5f..f89d9db26f9e 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -679,9 +679,7 @@ var BrowserApp = { if (tab) { let message = { type: "Content:LoadError", - tabID: tab.id, - uri: aBrowser.currentURI.spec, - title: aBrowser.contentTitle + tabID: tab.id }; sendMessageToJava(message); dump("Handled load error: " + e) @@ -2923,9 +2921,7 @@ Tab.prototype = { } catch(e) { let message = { type: "Content:LoadError", - tabID: this.id, - uri: this.browser.currentURI.spec, - title: this.browser.contentTitle + tabID: this.id }; sendMessageToJava(message); dump("Handled load error: " + e); From 494ca2a7d587ab0cb676ead5bc22b6f6ff77a991 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Sun, 24 Feb 2013 20:51:05 -0800 Subject: [PATCH 43/66] Bug 842883 - (Part 4) Replace DOMWindowClose event with Tab:Close. r=mfinkle --- mobile/android/base/Tabs.java | 3 --- mobile/android/chrome/content/browser.js | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 046d2cacc629..1eca5772b315 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -76,7 +76,6 @@ public class Tabs implements GeckoEventListener { registerEventListener("Content:PageShow"); registerEventListener("DOMTitleChanged"); registerEventListener("DOMLinkAdded"); - registerEventListener("DOMWindowClose"); } public void attachToActivity(GeckoApp activity) { @@ -355,8 +354,6 @@ public class Tabs implements GeckoEventListener { } else if (event.equals("DOMLinkAdded")) { tab.updateFaviconURL(message.getString("href"), message.getInt("size")); notifyListeners(tab, TabEvents.LINK_ADDED); - } else if (event.equals("DOMWindowClose")) { - closeTab(tab); } } catch (Exception e) { Log.w(LOGTAG, "handleMessage threw for " + event, e); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index f89d9db26f9e..422197efafd2 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3510,7 +3510,7 @@ Tab.prototype = { aEvent.preventDefault(); sendMessageToJava({ - type: "DOMWindowClose", + type: "Tab:Close", tabID: this.id }); } From 4f7d3fdf51899f242b66bd383f944702671fda06 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 20 Feb 2013 21:13:25 -0800 Subject: [PATCH 44/66] Bug 843462 (part 1) - Make Vector::insert() less error-prone. r=luke. --HG-- extra : rebase_source : 8cc2ba2a01d7d4970aacb7728bb2936ea473dcf5 --- js/public/Vector.h | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/js/public/Vector.h b/js/public/Vector.h index 0a625e346a0f..66f16b78d1a8 100644 --- a/js/public/Vector.h +++ b/js/public/Vector.h @@ -479,10 +479,19 @@ class Vector : private AllocPolicy void replaceRawBuffer(T *p, size_t length); /* - * Places |val| at position |p|, shifting existing elements - * from |p| onward one position higher. + * Places |val| at position |p|, shifting existing elements from |p| + * onward one position higher. On success, |p| should not be reused + * because it will be a dangling pointer if reallocation of the vector + * storage occurred; the return value should be used instead. On failure, + * NULL is returned. + * + * Example usage: + * + * if (!(p = vec.insert(p, val))) + * + * */ - bool insert(T *p, const T &val); + T *insert(T *p, const T &val); /* * Removes the element |t|, which must fall in the bounds [begin, end), @@ -863,24 +872,25 @@ Vector::internalAppendN(const T &t, size_t needed) } template -inline bool +inline T * Vector::insert(T *p, const T &val) { JS_ASSERT(begin() <= p && p <= end()); size_t pos = p - begin(); JS_ASSERT(pos <= mLength); size_t oldLength = mLength; - if (pos == oldLength) - return append(val); - { + if (pos == oldLength) { + if (!append(val)) + return NULL; + } else { T oldBack = back(); if (!append(oldBack)) /* Dup the last element. */ - return false; + return NULL; + for (size_t i = oldLength; i > pos; --i) + (*this)[i] = (*this)[i - 1]; + (*this)[pos] = val; } - for (size_t i = oldLength; i > pos; --i) - (*this)[i] = (*this)[i - 1]; - (*this)[pos] = val; - return true; + return begin() + pos; } template From 9c183b485f5505ce3bc15c8b63ad943f7166a56e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 20 Feb 2013 21:13:26 -0800 Subject: [PATCH 45/66] Bug 843462 (part 2) - Use a Vector to build the source notes. r=jorendorff. --HG-- extra : rebase_source : 57d50732d55b9e73bad04ec0c01b58b20de275b3 --- js/src/frontend/BytecodeEmitter.cpp | 184 +++++++++------------------- js/src/frontend/BytecodeEmitter.h | 38 +++--- 2 files changed, 79 insertions(+), 143 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 70d6cf777342..af70615f2cc9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -48,11 +48,9 @@ /* Allocation chunk counts, must be powers of two in general. */ #define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */ -#define SRCNOTE_CHUNK_LENGTH 1024 /* initial srcnote chunk length */ /* Macros to compute byte sizes from typed element counts. */ #define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) -#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) using namespace js; using namespace js::gc; @@ -100,12 +98,16 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared : sc(sc), parent(parent), script(sc->context, script), + prolog(sc->context, lineno), + main(sc->context, lineno), + current(&main), parser(parser), evalCaller(evalCaller), topStmt(NULL), topScopeStmt(NULL), blockChain(sc->context), atomIndices(sc->context), + firstLine(lineno), stackDepth(0), maxStackDepth(0), tryNoteList(sc->context), arrayCompDepth(0), @@ -118,10 +120,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, Shared hasGlobalScope(hasGlobalScope), selfHostingMode(selfHostingMode) { - memset(&prolog, 0, sizeof prolog); - memset(&main, 0, sizeof main); - current = &main; - firstLine = prolog.currentLine = main.currentLine = lineno; } bool @@ -133,9 +131,7 @@ BytecodeEmitter::init() BytecodeEmitter::~BytecodeEmitter() { js_free(prolog.base); - js_free(prolog.notes); js_free(main.base); - js_free(main.notes); } static ptrdiff_t @@ -2370,10 +2366,10 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) unsigned noteCount, noteCountDelta; /* Switch note's second offset is to first JSOP_CASE. */ - noteCount = bce->noteCount(); + noteCount = bce->notes().length(); if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, off - top)) return false; - noteCountDelta = bce->noteCount() - noteCount; + noteCountDelta = bce->notes().length() - noteCount; if (noteCountDelta != 0) caseNoteIndex += noteCountDelta; beforeCases = false; @@ -5905,78 +5901,51 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } static int -AllocSrcNote(JSContext *cx, BytecodeEmitter *bce) +AllocSrcNote(JSContext *cx, SrcNotesVector ¬es) { - jssrcnote *notes = bce->notes(); - jssrcnote *newnotes; - unsigned index = bce->noteCount(); - unsigned max = bce->noteLimit(); - - if (index == max) { - size_t newlength; - if (!notes) { - JS_ASSERT(!index && !max); - newlength = SRCNOTE_CHUNK_LENGTH; - newnotes = (jssrcnote *) cx->malloc_(SRCNOTE_SIZE(newlength)); - } else { - JS_ASSERT(index <= max); - newlength = max * 2; - newnotes = (jssrcnote *) cx->realloc_(notes, SRCNOTE_SIZE(newlength)); - } - if (!newnotes) { - js_ReportOutOfMemory(cx); - return -1; - } - bce->current->notes = newnotes; - bce->current->noteLimit = newlength; + jssrcnote dummy = 0; + if (!notes.append(dummy)) { + js_ReportOutOfMemory(cx); + return -1; } - - bce->current->noteCount = index + 1; - return (int)index; + return notes.length() - 1; } int frontend::NewSrcNote(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type) { - int index, n; - jssrcnote *sn; - ptrdiff_t offset, delta, xdelta; + SrcNotesVector ¬es = bce->notes(); + int index; - /* - * Claim a note slot in bce->notes() by growing it if necessary and then - * incrementing bce->noteCount(). - */ - index = AllocSrcNote(cx, bce); + index = AllocSrcNote(cx, notes); if (index < 0) return -1; - sn = &bce->notes()[index]; /* * Compute delta from the last annotated bytecode's offset. If it's too * big to fit in sn, allocate one or more xdelta notes and reset sn. */ - offset = bce->offset(); - delta = offset - bce->lastNoteOffset(); + ptrdiff_t offset = bce->offset(); + ptrdiff_t delta = offset - bce->lastNoteOffset(); bce->current->lastNoteOffset = offset; if (delta >= SN_DELTA_LIMIT) { do { - xdelta = Min(delta, SN_XDELTA_MASK); - SN_MAKE_XDELTA(sn, xdelta); + ptrdiff_t xdelta = Min(delta, SN_XDELTA_MASK); + SN_MAKE_XDELTA(¬es[index], xdelta); delta -= xdelta; - index = AllocSrcNote(cx, bce); + index = AllocSrcNote(cx, notes); if (index < 0) return -1; - sn = &bce->notes()[index]; } while (delta >= SN_DELTA_LIMIT); } /* * Initialize type and delta, then allocate the minimum number of notes * needed for type's arity. Usually, we won't need more, but if an offset - * does take two bytes, SetSrcNoteOffset will grow bce->notes(). + * does take two bytes, SetSrcNoteOffset will grow notes. */ - SN_MAKE_NOTE(sn, type, delta); - for (n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) { + SN_MAKE_NOTE(¬es[index], type, delta); + for (int n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) { if (NewSrcNote(cx, bce, SRC_NULL) < 0) return -1; } @@ -6012,26 +5981,9 @@ frontend::NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptr return index; } -static bool -GrowSrcNotes(JSContext *cx, BytecodeEmitter *bce) -{ - size_t newlength = bce->noteLimit() * 2; - jssrcnote *newnotes = (jssrcnote *) cx->realloc_(bce->notes(), newlength); - if (!newnotes) { - js_ReportOutOfMemory(cx); - return false; - } - bce->current->notes = newnotes; - bce->current->noteLimit = newlength; - return true; -} - -jssrcnote * +bool frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta) { - ptrdiff_t base, limit, newdelta, diff; - int index; - /* * Called only from FinishTakingSrcNotes to add to main script note * deltas, and only by a small positive amount. @@ -6039,40 +5991,33 @@ frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, JS_ASSERT(bce->current == &bce->main); JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT); - base = SN_DELTA(sn); - limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; - newdelta = base + delta; + ptrdiff_t base = SN_DELTA(sn); + ptrdiff_t limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; + ptrdiff_t newdelta = base + delta; if (newdelta < limit) { SN_SET_DELTA(sn, newdelta); } else { - index = sn - bce->main.notes; - if (bce->main.noteCount == bce->main.noteLimit) { - if (!GrowSrcNotes(cx, bce)) - return NULL; - sn = bce->main.notes + index; - } - diff = bce->main.noteCount - index; - bce->main.noteCount++; - memmove(sn + 1, sn, SRCNOTE_SIZE(diff)); - SN_MAKE_XDELTA(sn, delta); - sn++; + jssrcnote xdelta; + SN_MAKE_XDELTA(&xdelta, delta); + if (!(sn = bce->main.notes.insert(sn, xdelta))) + return false; } - return sn; + return true; } static bool -SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset) +SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, + ptrdiff_t offset) { - jssrcnote *sn; - ptrdiff_t diff; - if (size_t(offset) > SN_MAX_OFFSET) { ReportStatementTooLarge(cx, bce->topStmt); return false; } + SrcNotesVector ¬es = bce->notes(); + /* Find the offset numbered which (i.e., skip exactly which offsets). */ - sn = &bce->notes()[index]; + jssrcnote *sn = notes.begin() + index; JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity); for (sn++; which; sn++, which--) { @@ -6088,25 +6033,14 @@ SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned w if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK || (*sn & SN_3BYTE_OFFSET_FLAG)) { /* Maybe this offset was already set to a three-byte value. */ if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { - /* Losing, need to insert another two bytes for this offset. */ - index = sn - bce->notes(); - - /* - * Test to see if the source note array must grow to accommodate - * either the first or second byte of additional storage required - * by this 3-byte offset. - */ - if (bce->noteCount() + 1 >= bce->noteLimit()) { - if (!GrowSrcNotes(cx, bce)) - return false; - sn = bce->notes() + index; + /* Insert two dummy bytes that will be overwritten shortly. */ + jssrcnote dummy = 0; + if (!(sn = notes.insert(sn, dummy)) || + !(sn = notes.insert(sn, dummy))) + { + js_ReportOutOfMemory(cx); + return false; } - bce->current->noteCount += 2; - - diff = bce->noteCount() - (index + 3); - JS_ASSERT(diff >= 0); - if (diff > 0) - memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff)); } *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); *sn++ = (jssrcnote)(offset >> 8); @@ -6156,18 +6090,14 @@ DumpSrcNoteSizeHist() bool frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes) { - unsigned prologCount, mainCount, totalCount; - ptrdiff_t offset, delta; - jssrcnote *sn; - JS_ASSERT(bce->current == &bce->main); - prologCount = bce->prolog.noteCount; + unsigned prologCount = bce->prolog.notes.length(); if (prologCount && bce->prolog.currentLine != bce->firstLine) { bce->switchToProlog(); if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0) return false; - prologCount = bce->prolog.noteCount; + prologCount = bce->prolog.notes.length(); bce->switchToMain(); } else { /* @@ -6177,14 +6107,14 @@ frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *n * prepending SRC_XDELTA notes to it to account for prolog bytecodes * that came at and after the last annotated bytecode. */ - offset = bce->prologOffset() - bce->prolog.lastNoteOffset; + ptrdiff_t offset = bce->prologOffset() - bce->prolog.lastNoteOffset; JS_ASSERT(offset >= 0); - if (offset > 0 && bce->main.noteCount != 0) { + if (offset > 0 && bce->main.notes.length() != 0) { /* NB: Use as much of the first main note's delta as we can. */ - sn = bce->main.notes; - delta = SN_IS_XDELTA(sn) - ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) - : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); + jssrcnote *sn = bce->main.notes.begin(); + ptrdiff_t delta = SN_IS_XDELTA(sn) + ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) + : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); if (offset < delta) delta = offset; for (;;) { @@ -6194,16 +6124,16 @@ frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *n if (offset == 0) break; delta = Min(offset, SN_XDELTA_MASK); - sn = bce->main.notes; + sn = bce->main.notes.begin(); } } } - mainCount = bce->main.noteCount; - totalCount = prologCount + mainCount; + unsigned mainCount = bce->main.notes.length(); + unsigned totalCount = prologCount + mainCount; if (prologCount) - PodCopy(notes, bce->prolog.notes, prologCount); - PodCopy(notes + prologCount, bce->main.notes, mainCount); + PodCopy(notes, bce->prolog.notes.begin(), prologCount); + PodCopy(notes + prologCount, bce->main.notes.begin(), mainCount); SN_MAKE_TERMINATOR(¬es[totalCount]); return true; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index a4a10d92c6e6..282d76f5766d 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -59,6 +59,10 @@ class CGConstList { struct StmtInfoBCE; +// Use zero inline elements because these go on the stack and affect how many +// nested functions are possible. +typedef Vector SrcNotesVector; + struct BytecodeEmitter { typedef StmtInfoBCE StmtInfo; @@ -69,18 +73,24 @@ struct BytecodeEmitter Rooted script; /* the JSScript we're ultimately producing */ - struct { + struct EmitSection { jsbytecode *base; /* base of JS bytecode vector */ jsbytecode *limit; /* one byte beyond end of bytecode */ jsbytecode *next; /* pointer to next free bytecode */ - jssrcnote *notes; /* source notes, see below */ - unsigned noteCount; /* number of source notes so far */ - unsigned noteLimit; /* limit number for source notes in notePool */ + SrcNotesVector notes; /* source notes, see below */ ptrdiff_t lastNoteOffset; /* code offset for last source note */ unsigned currentLine; /* line number for tree-based srcnote gen */ unsigned lastColumn; /* zero-based column index on currentLine of last SRC_COLSPAN-annotated opcode */ - } prolog, main, *current; + + EmitSection(JSContext *cx, unsigned lineno) + : base(NULL), limit(NULL), next(NULL), notes(cx), lastNoteOffset(0), + currentLine(lineno), lastColumn(0) + { + notes.reserve(1024); + } + }; + EmitSection prolog, main, *current; Parser *const parser; /* the parser */ @@ -176,9 +186,7 @@ struct BytecodeEmitter void switchToMain() { current = &main; } void switchToProlog() { current = &prolog; } - jssrcnote *notes() const { return current->notes; } - unsigned noteCount() const { return current->noteCount; } - unsigned noteLimit() const { return current->noteLimit; } + SrcNotesVector ¬es() const { return current->notes; } ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; } unsigned currentLine() const { return current->currentLine; } unsigned lastColumn() const { return current->lastColumn; } @@ -379,10 +387,8 @@ int NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1, ptrdiff_t offset2); -/* - * NB: this function can add at most one extra extended delta note. - */ -jssrcnote * +/* NB: this function can add at most one extra extended delta note. */ +bool AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta); bool @@ -402,14 +408,14 @@ inline ptrdiff_t BytecodeEmitter::countFinalSourceNotes() { ptrdiff_t diff = prologOffset() - prolog.lastNoteOffset; - ptrdiff_t cnt = prolog.noteCount + main.noteCount + 1; - if (prolog.noteCount && prolog.currentLine != firstLine) { + ptrdiff_t cnt = prolog.notes.length() + main.notes.length() + 1; + if (prolog.notes.length() && prolog.currentLine != firstLine) { if (diff > SN_DELTA_MASK) cnt += JS_HOWMANY(diff - SN_DELTA_MASK, SN_XDELTA_MASK); cnt += 2 + ((firstLine > SN_3BYTE_OFFSET_MASK) << 1); } else if (diff > 0) { - if (main.noteCount) { - jssrcnote *sn = main.notes; + if (main.notes.length()) { + jssrcnote *sn = main.notes.begin(); diff -= SN_IS_XDELTA(sn) ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); From 23730fbeba46058ad08a1d82d69086904a82c96b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 20 Feb 2013 21:13:28 -0800 Subject: [PATCH 46/66] Bug 843462 (part 3) - Use a Vector to build the bytecode. r=jorendorff. --HG-- extra : rebase_source : b5f266d41f94daf626540f40986f22a1f26c0556 --- js/src/frontend/BytecodeEmitter.cpp | 163 +++++++++++----------------- js/src/frontend/BytecodeEmitter.h | 34 +++--- js/src/jsscript.cpp | 4 +- 3 files changed, 80 insertions(+), 121 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index af70615f2cc9..9ac2281614a4 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -46,12 +46,6 @@ #include "frontend/SharedContext-inl.h" #include "vm/Shape-inl.h" -/* Allocation chunk counts, must be powers of two in general. */ -#define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */ - -/* Macros to compute byte sizes from typed element counts. */ -#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) - using namespace js; using namespace js::gc; using namespace js::frontend; @@ -130,43 +124,17 @@ BytecodeEmitter::init() BytecodeEmitter::~BytecodeEmitter() { - js_free(prolog.base); - js_free(main.base); } static ptrdiff_t EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta) { - jsbytecode *base = bce->base(); - jsbytecode *newbase; - jsbytecode *next = bce->next(); - jsbytecode *limit = bce->limit(); - ptrdiff_t offset = next - base; - size_t minlength = offset + delta; + ptrdiff_t offset = bce->code().length(); - if (next + delta > limit) { - size_t newlength; - if (!base) { - JS_ASSERT(!next && !limit); - newlength = BYTECODE_CHUNK_LENGTH; - if (newlength < minlength) /* make it bigger if necessary */ - newlength = RoundUpPow2(minlength); - newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength)); - } else { - JS_ASSERT(base <= next && next <= limit); - newlength = (limit - base) * 2; - if (newlength < minlength) /* make it bigger if necessary */ - newlength = RoundUpPow2(minlength); - newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength)); - } - if (!newbase) { - js_ReportOutOfMemory(cx); - return -1; - } - JS_ASSERT(newlength >= size_t(offset + delta)); - bce->current->base = newbase; - bce->current->limit = newbase + newlength; - bce->current->next = newbase + offset; + jsbytecode dummy = 0; + if (!bce->code().appendN(dummy, delta)) { + js_ReportOutOfMemory(cx); + return -1; } return offset; } @@ -186,7 +154,6 @@ UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target) JSOp op = (JSOp) *pc; const JSCodeSpec *cs = &js_CodeSpec[op]; - if (cs->format & JOF_TMPSLOT_MASK) { /* * An opcode may temporarily consume stack space during execution. @@ -235,11 +202,12 @@ ptrdiff_t frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op) { ptrdiff_t offset = EmitCheck(cx, bce, 1); + if (offset < 0) + return -1; - if (offset >= 0) { - *bce->current->next++ = (jsbytecode)op; - UpdateDepth(cx, bce, offset); - } + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + UpdateDepth(cx, bce, offset); return offset; } @@ -247,14 +215,13 @@ ptrdiff_t frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1) { ptrdiff_t offset = EmitCheck(cx, bce, 2); + if (offset < 0) + return -1; - if (offset >= 0) { - jsbytecode *next = bce->next(); - next[0] = (jsbytecode)op; - next[1] = op1; - bce->current->next = next + 2; - UpdateDepth(cx, bce, offset); - } + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + code[1] = op1; + UpdateDepth(cx, bce, offset); return offset; } @@ -267,15 +234,14 @@ frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, JS_ASSERT(!IsLocalOp(op)); ptrdiff_t offset = EmitCheck(cx, bce, 3); + if (offset < 0) + return -1; - if (offset >= 0) { - jsbytecode *next = bce->next(); - next[0] = (jsbytecode)op; - next[1] = op1; - next[2] = op2; - bce->current->next = next + 3; - UpdateDepth(cx, bce, offset); - } + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + code[1] = op1; + code[2] = op2; + UpdateDepth(cx, bce, offset); return offset; } @@ -284,20 +250,20 @@ frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra) { ptrdiff_t length = 1 + (ptrdiff_t)extra; ptrdiff_t offset = EmitCheck(cx, bce, length); + if (offset < 0) + return -1; - if (offset >= 0) { - jsbytecode *next = bce->next(); - *next = (jsbytecode)op; - memset(next + 1, 0, BYTECODE_SIZE(extra)); - bce->current->next = next + length; + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + /* The remaining |extra| bytes are set by the caller */ + + /* + * Don't UpdateDepth if op's use-count comes from the immediate + * operand yet to be stored in the extra bytes after op. + */ + if (js_CodeSpec[op].nuses >= 0) + UpdateDepth(cx, bce, offset); - /* - * Don't UpdateDepth if op's use-count comes from the immediate - * operand yet to be stored in the extra bytes after op. - */ - if (js_CodeSpec[op].nuses >= 0) - UpdateDepth(cx, bce, offset); - } return offset; } @@ -305,14 +271,13 @@ static ptrdiff_t EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off) { ptrdiff_t offset = EmitCheck(cx, bce, 5); + if (offset < 0) + return -1; - if (offset >= 0) { - jsbytecode *next = bce->next(); - next[0] = (jsbytecode)op; - SET_JUMP_OFFSET(next, off); - bce->current->next = next + 5; - UpdateDepth(cx, bce, offset); - } + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + SET_JUMP_OFFSET(code, off); + UpdateDepth(cx, bce, offset); return offset; } @@ -710,7 +675,7 @@ PopStatementBCE(JSContext *cx, BytecodeEmitter *bce) { StmtInfoBCE *stmt = bce->topStmt; if (!stmt->isTrying() && - (!BackPatch(cx, bce, stmt->breaks, bce->next(), JSOP_GOTO) || + (!BackPatch(cx, bce, stmt->breaks, bce->code().end(), JSOP_GOTO) || !BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO))) { return false; @@ -728,10 +693,9 @@ EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce) if (offset < 0) return false; - jsbytecode *next = bce->next(); - next[0] = jsbytecode(op); - SET_UINT32_INDEX(next, index); - bce->current->next = next + len; + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + SET_UINT32_INDEX(code, index); UpdateDepth(cx, bce, offset); CheckTypeSet(cx, bce, op); return true; @@ -746,10 +710,9 @@ EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce) if (offset < 0) return false; - jsbytecode *next = bce->next(); - next[0] = jsbytecode(op); - SET_UINT32_INDEX(next, index); - bce->current->next = next + len; + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + SET_UINT32_INDEX(code, index); UpdateDepth(cx, bce, offset); CheckTypeSet(cx, bce, op); return true; @@ -795,10 +758,9 @@ EmitAtomIncDec(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce) if (offset < 0) return false; - jsbytecode *next = bce->next(); - next[0] = jsbytecode(op); - SET_UINT32_INDEX(next, index); - bce->current->next = next + len; + jsbytecode *code = bce->code(offset); + code[0] = jsbytecode(op); + SET_UINT32_INDEX(code, index); UpdateDepth(cx, bce, offset); CheckTypeSet(cx, bce, op); return true; @@ -2502,7 +2464,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod FunctionBox *funbox = bce->sc->asFunctionBox(); if (funbox->argumentsHasLocalBinding()) { - JS_ASSERT(bce->next() == bce->base()); /* See JSScript::argumentsBytecode. */ + JS_ASSERT(bce->offset() == 0); /* See JSScript::argumentsBytecode. */ bce->switchToProlog(); if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0) return false; @@ -3418,13 +3380,12 @@ EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn) if (offset < 0) return false; - jsbytecode *next = bce->next(); - next[0] = JSOP_NEWINIT; - next[1] = jsbytecode(key); - next[2] = 0; - next[3] = 0; - next[4] = 0; - bce->current->next = next + len; + jsbytecode *code = bce->code(offset); + code[0] = JSOP_NEWINIT; + code[1] = jsbytecode(key); + code[2] = 0; + code[3] = 0; + code[4] = 0; UpdateDepth(cx, bce, offset); CheckTypeSet(cx, bce, JSOP_NEWINIT); return true; @@ -3804,7 +3765,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) * Fix up the gosubs that might have been emitted before non-local * jumps to the finally code. */ - if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->next(), JSOP_GOSUB)) + if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->code().end(), JSOP_GOSUB)) return false; finallyStart = bce->offset(); @@ -3829,7 +3790,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; /* Fix up the end-of-try/catch jumps to come here. */ - if (!BackPatch(cx, bce, catchJump, bce->next(), JSOP_GOTO)) + if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO)) return false; /* @@ -4649,7 +4610,7 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (!EmitNonLocalJumpFixup(cx, bce, NULL)) return false; if (top + JSOP_RETURN_LENGTH != bce->offset()) { - bce->base()[top] = JSOP_SETRVAL; + bce->code()[top] = JSOP_SETRVAL; if (Emit1(cx, bce, JSOP_RETRVAL) < 0) return false; } @@ -5227,7 +5188,7 @@ EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) * ignore setters and to avoid dup'ing and popping the object as each * property is added, as JSOP_SETELEM/JSOP_SETPROP would do. */ - ptrdiff_t offset = bce->next() - bce->base(); + ptrdiff_t offset = bce->offset(); if (!EmitNewInit(cx, bce, JSProto_Object, pn)) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 282d76f5766d..0baf03d2e0b6 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -61,6 +61,7 @@ struct StmtInfoBCE; // Use zero inline elements because these go on the stack and affect how many // nested functions are possible. +typedef Vector BytecodeVector; typedef Vector SrcNotesVector; struct BytecodeEmitter @@ -74,9 +75,7 @@ struct BytecodeEmitter Rooted script; /* the JSScript we're ultimately producing */ struct EmitSection { - jsbytecode *base; /* base of JS bytecode vector */ - jsbytecode *limit; /* one byte beyond end of bytecode */ - jsbytecode *next; /* pointer to next free bytecode */ + BytecodeVector code; /* bytecode */ SrcNotesVector notes; /* source notes, see below */ ptrdiff_t lastNoteOffset; /* code offset for last source note */ unsigned currentLine; /* line number for tree-based srcnote gen */ @@ -84,9 +83,11 @@ struct BytecodeEmitter last SRC_COLSPAN-annotated opcode */ EmitSection(JSContext *cx, unsigned lineno) - : base(NULL), limit(NULL), next(NULL), notes(cx), lastNoteOffset(0), - currentLine(lineno), lastColumn(0) + : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineno), lastColumn(0) { + // Start them off moderately large, to avoid repeated resizings + // early on. + code.reserve(1024); notes.reserve(1024); } }; @@ -136,17 +137,17 @@ struct BytecodeEmitter don't ever get emitted. See the comment for the field |selfHostingMode| in Parser.h for details. */ + /* + * Note that BytecodeEmitters are magic: they own the arena "top-of-stack" + * space above their tempMark points. This means that you cannot alloc from + * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter + * destruction. + */ BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc, HandleScript script, HandleScript evalCaller, bool hasGlobalScope, unsigned lineno, bool selfHostingMode = false); bool init(); - /* - * Note that BytecodeEmitters are magic: they own the arena "top-of-stack" - * space above their tempMark points. This means that you cannot alloc from - * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter - * destructor call. - */ ~BytecodeEmitter(); bool isAliasedName(ParseNode *pn); @@ -176,13 +177,10 @@ struct BytecodeEmitter TokenStream *tokenStream() { return &parser->tokenStream; } - jsbytecode *base() const { return current->base; } - jsbytecode *limit() const { return current->limit; } - jsbytecode *next() const { return current->next; } - jsbytecode *code(ptrdiff_t offset) const { return base() + offset; } - ptrdiff_t offset() const { return next() - base(); } - jsbytecode *prologBase() const { return prolog.base; } - ptrdiff_t prologOffset() const { return prolog.next - prolog.base; } + BytecodeVector &code() const { return current->code; } + jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; } + ptrdiff_t offset() const { return current->code.end() - current->code.begin(); } + ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); } void switchToMain() { current = &main; } void switchToProlog() { current = &prolog; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 4aad736ef6f0..aff9ab415ce1 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1874,8 +1874,8 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle script, Bytecode return false; jsbytecode *code = ssd->data; - PodCopy(code, bce->prologBase(), prologLength); - PodCopy(code + prologLength, bce->base(), mainLength); + PodCopy(code, bce->prolog.code.begin(), prologLength); + PodCopy(code + prologLength, bce->code().begin(), mainLength); if (!FinishTakingSrcNotes(cx, bce, (jssrcnote *)(code + script->length))) return false; InitAtomMap(cx, bce->atomIndices.getMap(), ssd->atoms(script->length, nsrcnotes)); From 259242f46c452dc1c46afadd1933c14bebb2f3a8 Mon Sep 17 00:00:00 2001 From: Kannan Vijayan Date: Mon, 25 Feb 2013 00:57:39 -0500 Subject: [PATCH 47/66] Bug 844253 - Change useNewType to be a flag on StackFrame instead of an explicitly passed argument. r=bhackett --- js/src/ion/Ion.cpp | 5 ++--- js/src/ion/Ion.h | 3 +-- js/src/jsinterp.cpp | 25 +++++++++++++++++++++---- js/src/methodjit/InvokeHelpers.cpp | 4 +++- js/src/methodjit/StubCalls.cpp | 3 +-- js/src/vm/Stack-inl.h | 7 +++++++ js/src/vm/Stack.cpp | 4 ++-- js/src/vm/Stack.h | 24 +++++++++++++++++------- 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index b5bb7452bc84..bd91164fceee 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -1472,8 +1472,7 @@ ion::CanEnterAtBranch(JSContext *cx, JSScript *script, AbstractFramePtr fp, } MethodStatus -ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, - bool isConstructing, bool newType) +ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConstructing) { JS_ASSERT(ion::IsEnabled(cx)); @@ -1495,7 +1494,7 @@ ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, if (isConstructing && fp.thisValue().isPrimitive()) { RootedScript scriptRoot(cx, script); RootedObject callee(cx, &fp.callee()); - RootedObject obj(cx, CreateThisForFunction(cx, callee, newType)); + RootedObject obj(cx, CreateThisForFunction(cx, callee, fp.useNewType())); if (!obj) return Method_Skipped; fp.thisValue().setObject(*obj); diff --git a/js/src/ion/Ion.h b/js/src/ion/Ion.h index 407e09cabb8e..c7b069a95cfd 100644 --- a/js/src/ion/Ion.h +++ b/js/src/ion/Ion.h @@ -261,8 +261,7 @@ bool SetIonContext(IonContext *ctx); MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script, AbstractFramePtr fp, jsbytecode *pc, bool isConstructing); -MethodStatus CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, - bool isConstructing, bool newType); +MethodStatus CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConstructing); MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs); enum IonExecStatus diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 16a27833f51e..e1a6c2fc8b40 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -276,6 +276,20 @@ js::RunScript(JSContext *cx, StackFrame *fp) JS_CHECK_RECURSION(cx, return false); + // Check to see if useNewType flag should be set for this frame. + if (fp->isFunctionFrame() && fp->isConstructing() && !fp->isGeneratorFrame()) { + StackIter iter(cx); + if (!iter.done()) { + ++iter; + if (iter.isScript()) { + RawScript script = iter.script(); + jsbytecode *pc = iter.pc(); + if (UseNewType(cx, script, pc)) + fp->setUseNewType(); + } + } + } + #ifdef DEBUG struct CheckStackBalance { JSContext *cx; @@ -294,7 +308,7 @@ js::RunScript(JSContext *cx, StackFrame *fp) #ifdef JS_ION if (ion::IsEnabled(cx)) { ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(fp), - fp->isConstructing(), false); + fp->isConstructing()); if (status == ion::Method_Error) return false; if (status == ion::Method_Compiled) { @@ -1159,7 +1173,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) if (interpMode == JSINTERP_NORMAL) { StackFrame *fp = regs.fp(); if (!fp->isGeneratorFrame()) { - if (!fp->prologue(cx, UseNewTypeAtEntry(cx, fp))) + if (!fp->prologue(cx)) goto error; } else { Probes::enterScript(cx, script, script->function(), fp); @@ -2361,6 +2375,9 @@ BEGIN_CASE(JSOP_FUNCALL) funScript = fun->nonLazyScript(); if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial)) goto error; + + if (newType) + regs.fp()->setUseNewType(); SET_SCRIPT(regs.fp()->script()); #ifdef JS_METHODJIT @@ -2370,7 +2387,7 @@ BEGIN_CASE(JSOP_FUNCALL) #ifdef JS_ION if (!newType && ion::IsEnabled(cx)) { ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(regs.fp()), - regs.fp()->isConstructing(), newType); + regs.fp()->isConstructing()); if (status == ion::Method_Error) goto error; if (status == ion::Method_Compiled) { @@ -2405,7 +2422,7 @@ BEGIN_CASE(JSOP_FUNCALL) } #endif - if (!regs.fp()->prologue(cx, newType)) + if (!regs.fp()->prologue(cx)) goto error; if (cx->compartment->debugMode()) { switch (ScriptDebugPrologue(cx, regs.fp())) { diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 01677beaf98e..0c1ee81d9b36 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -940,7 +940,9 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM return js_InternalThrow(f); fp->initVarsToUndefined(); fp->scopeChain(); - if (!fp->prologue(cx, types::UseNewTypeAtEntry(cx, fp))) + if (types::UseNewTypeAtEntry(cx, fp)) + fp->setUseNewType(); + if (!fp->prologue(cx)) return js_InternalThrow(f); /* diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 3f82f21e251f..496a6a59a159 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -808,8 +808,7 @@ stubs::TriggerIonCompile(VMFrame &f) compileStatus = ion::CanEnterAtBranch(f.cx, script, f.cx->fp(), osrPC, f.fp()->isConstructing()); } else { - compileStatus = ion::CanEnter(f.cx, script, f.cx->fp(), f.fp()->isConstructing(), - /* newType = */ false); + compileStatus = ion::CanEnter(f.cx, script, f.cx->fp(), f.fp()->isConstructing()); } if (compileStatus != ion::Method_Compiled) { diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 1ca3b328c7c1..ae947ae5ff66 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -738,6 +738,13 @@ AbstractFramePtr::hasCallObj() const return false; } inline bool +AbstractFramePtr::useNewType() const +{ + if (isStackFrame()) + return asStackFrame()->useNewType(); + return false; +} +inline bool AbstractFramePtr::isGeneratorFrame() const { if (isStackFrame()) diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 71020d99c335..d6ae424492d6 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -307,7 +307,7 @@ StackFrame::initFunctionScopeObjects(JSContext *cx) } bool -StackFrame::prologue(JSContext *cx, bool newType) +StackFrame::prologue(JSContext *cx) { RootedScript script(cx, this->script()); @@ -339,7 +339,7 @@ StackFrame::prologue(JSContext *cx, bool newType) if (isConstructing()) { RootedObject callee(cx, &this->callee()); - JSObject *obj = CreateThisForFunction(cx, callee, newType); + JSObject *obj = CreateThisForFunction(cx, callee, useNewType()); if (!obj) return false; functionThis() = ObjectValue(*obj); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 7241bb39739c..2f37e0909e41 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -297,6 +297,7 @@ class AbstractFramePtr inline bool hasArgsObj() const; inline ArgumentsObject &argsObj() const; inline void initArgsObj(ArgumentsObject &argsobj) const; + inline bool useNewType() const; inline bool copyRawFrameSlots(AutoValueVector *vec) const; @@ -389,10 +390,13 @@ class StackFrame HAS_PUSHED_SPS_FRAME = 0x100000, /* SPS was notified of enty */ /* Ion frame state */ - RUNNING_IN_ION = 0x200000, /* frame is running in Ion */ - CALLING_INTO_ION = 0x400000, /* frame is calling into Ion */ + RUNNING_IN_ION = 0x200000, /* frame is running in Ion */ + CALLING_INTO_ION = 0x400000, /* frame is calling into Ion */ - JIT_REVISED_STACK = 0x800000 /* sp was revised by JIT for lowered apply */ + JIT_REVISED_STACK = 0x800000, /* sp was revised by JIT for lowered apply */ + + /* Miscellaneous state. */ + USE_NEW_TYPE = 0x1000000 /* Use new type for constructed |this| object. */ }; private: @@ -489,12 +493,9 @@ class StackFrame * over-recursed) after pushing the stack frame but before 'prologue' is * called or completes fully. To simplify usage, 'epilogue' does not assume * 'prologue' has completed and handles all the intermediate state details. - * - * The 'newType' option indicates whether the constructed 'this' value (if - * there is one) should be given a new singleton type. */ - bool prologue(JSContext *cx, bool newType); + bool prologue(JSContext *cx); void epilogue(JSContext *cx); /* Subsets of 'prologue' called from jit code. */ @@ -1052,6 +1053,15 @@ class StackFrame return flags_ & HAS_ARGS_OBJ; } + void setUseNewType() { + JS_ASSERT(isConstructing()); + flags_ |= USE_NEW_TYPE; + } + bool useNewType() const { + JS_ASSERT(isConstructing()); + return flags_ & USE_NEW_TYPE; + } + /* * The method JIT call/apply optimization can erase Function.{call,apply} * invocations from the stack and push the callee frame directly. The base From e07370ca5d9618b9fc38f299e15fbd9c58111c96 Mon Sep 17 00:00:00 2001 From: Chia-hung Tai Date: Mon, 25 Feb 2013 14:08:23 +0800 Subject: [PATCH 48/66] Bug 810067 - Support automatic/manual/never retrieval modes. r=vyang --- b2g/chrome/content/settings.js | 5 ++ dom/mms/src/ril/MmsPduHelper.jsm | 3 + dom/mms/src/ril/MmsService.js | 109 +++++++++++++++++++++++++------ modules/libpref/src/init/all.js | 6 ++ 4 files changed, 102 insertions(+), 21 deletions(-) diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js index 349dc89bad97..636857fa437a 100644 --- a/b2g/chrome/content/settings.js +++ b/b2g/chrome/content/settings.js @@ -155,6 +155,11 @@ SettingsListener.observe('language.current', 'en-US', function(value) { }); }); + SettingsListener.observe('ril.mms.retrieval_mode', 'manual', + function(value) { + Services.prefs.setCharPref('dom.mms.retrieval_mode', value); + }); + SettingsListener.observe('ril.sms.strict7BitEncoding.enabled', false, function(value) { Services.prefs.setBoolPref('dom.sms.strict7BitEncoding', value); diff --git a/dom/mms/src/ril/MmsPduHelper.jsm b/dom/mms/src/ril/MmsPduHelper.jsm index 3c388fe98ca2..d9b5c33ca20c 100644 --- a/dom/mms/src/ril/MmsPduHelper.jsm +++ b/dom/mms/src/ril/MmsPduHelper.jsm @@ -1580,6 +1580,9 @@ const MMS_PDU_TYPES = (function () { "to", "date", "x-mms-status"]); + add(MMS_PDU_TYPE_ACKNOWLEDGE_IND, false, ["x-mms-message-type", + "x-mms-transaction-id", + "x-mms-mms-version"]); return pdus; })(); diff --git a/dom/mms/src/ril/MmsService.js b/dom/mms/src/ril/MmsService.js index 53ced4e77d51..a7b2700aa36a 100644 --- a/dom/mms/src/ril/MmsService.js +++ b/dom/mms/src/ril/MmsService.js @@ -34,6 +34,12 @@ const CONFIG_SEND_REPORT_ALWAYS = 3; const TIME_TO_BUFFER_MMS_REQUESTS = 30000; const TIME_TO_RELEASE_MMS_CONNECTION = 30000; + +const PREF_RETRIEVAL_MODE = 'dom.mms.retrieval_mode'; +const RETRIEVAL_MODE_MANUAL = "manual"; +const RETRIEVAL_MODE_AUTOMATIC = "automatic"; +const RETRIEVAL_MODE_NEVER = "never"; + XPCOMUtils.defineLazyServiceGetter(this, "gpps", "@mozilla.org/network/protocol-proxy-service;1", "nsIProtocolProxyService"); @@ -683,6 +689,48 @@ SendTransaction.prototype = { } }; +/** + * Send M-acknowledge.ind back to MMSC. + * + * @param transactionId + * X-Mms-Transaction-ID of the message. + * @param reportAllowed + * X-Mms-Report-Allowed of the response. + * + * @see OMA-TS-MMS_ENC-V1_3-20110913-A section 6.4 + */ +function AcknowledgeTransaction(transactionId, reportAllowed) { + let headers = {}; + + // Mandatory fields + headers["x-mms-message-type"] = MMS.MMS_PDU_TYPE_ACKNOWLEDGE_IND; + headers["x-mms-transaction-id"] = transactionId; + headers["x-mms-mms-version"] = MMS.MMS_VERSION; + // Optional fields + headers["x-mms-report-allowed"] = reportAllowed; + + this.istream = MMS.PduHelper.compose(null, {headers: headers}); +} +AcknowledgeTransaction.prototype = { + /** + * @param callback [optional] + * A callback function that takes one argument -- the http status. + */ + run: function run(callback) { + let requestCallback; + if (callback) { + requestCallback = function (httpStatus, data) { + // `The MMS Client SHOULD ignore the associated HTTP POST response + // from the MMS Proxy-Relay.` ~ OMA-TS-MMS_CTR-V1_3-20110913-A + // section 8.2.3 "Retrieving an MM". + callback(httpStatus); + }; + } + gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc, + this.istream, requestCallback); + } +}; + /** * MmsService */ @@ -755,34 +803,53 @@ MmsService.prototype = { */ handleNotificationIndication: function handleNotificationIndication(notification) { // TODO: bug 839436 - make DB be able to save MMS messages - // TODO: bug 810067 - support automatic/manual/never retrieval modes let url = notification.headers["x-mms-content-location"].uri; // TODO: bug 810091 - don't download message twice on receiving duplicated // notification - this.retrieveMessage(url, (function (mmsStatus, retrievedMsg) { - debug("retrievedMsg = " + JSON.stringify(retrievedMsg)); - if (this.isTransientError(mmsStatus)) { - // TODO: remove this check after bug 810097 is landed. - return; - } - let transactionId = notification.headers["x-mms-transaction-id"]; + let retrievalMode = RETRIEVAL_MODE_MANUAL; + try { + retrievalMode = Services.prefs.getCharPref(PREF_RETRIEVAL_MODE); + } catch (e) {} - // For X-Mms-Report-Allowed - let wish = notification.headers["x-mms-delivery-report"]; - // `The absence of the field does not indicate any default value.` - // So we go checking the same field in retrieved message instead. - if ((wish == null) && retrievedMsg) { - wish = retrievedMsg.headers["x-mms-delivery-report"]; - } - let reportAllowed = - this.getReportAllowed(this.confSendDeliveryReport, wish); + if (RETRIEVAL_MODE_AUTOMATIC === retrievalMode) { + this.retrieveMessage(url, (function responseNotify(mmsStatus, retrievedMsg) { + debug("retrievedMsg = " + JSON.stringify(retrievedMsg)); + if (this.isTransientError(mmsStatus)) { + // TODO: remove this check after bug 810097 is landed. + return; + } - let transaction = - new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed); - transaction.run(); - }).bind(this)); + let transactionId = notification.headers["x-mms-transaction-id"]; + + // For X-Mms-Report-Allowed + let wish = notification.headers["x-mms-delivery-report"]; + // `The absence of the field does not indicate any default value.` + // So we go checking the same field in retrieved message instead. + if ((wish == null) && retrievedMsg) { + wish = retrievedMsg.headers["x-mms-delivery-report"]; + } + let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish); + + let transaction = + new NotifyResponseTransaction(transactionId, mmsStatus, reportAllowed); + transaction.run(); + }).bind(this)); + return; + } + + let transactionId = notification.headers["x-mms-transaction-id"]; + let mmsStatus = RETRIEVAL_MODE_NEVER === retrievalMode ? + MMS.MMS_PDU_STATUS_REJECTED : MMS.MMS_PDU_STATUS_DEFERRED; + // For X-Mms-Report-Allowed + let wish = notification.headers["x-mms-delivery-report"]; + let reportAllowed = this.getReportAllowed(this.confSendDeliveryReport, wish); + + let transaction = new NotifyResponseTransaction(transactionId, + mmsStatus, + reportAllowed); + transaction.run(); }, /** diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 9c070aada205..3806027e5a55 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -4110,6 +4110,12 @@ pref("dom.placeholder.show_on_focus", true); pref("wap.UAProf.url", ""); pref("wap.UAProf.tagname", "x-wap-profile"); +//Retrieval mode for MMS +//manual: Manual retrieval mode. +//automatic: Automatic retrieval mode. +//never: Never retrieval mode. +pref("dom.mms.retrieval_mode", "manual"); + // If the user puts a finger down on an element and we think the user // might be executing a pan gesture, how long do we wait before // tentatively deciding the gesture is actually a tap and activating From 16e0bf818744ec566af119d4fcf98ae3eed8deec Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Sun, 24 Feb 2013 22:52:40 -0800 Subject: [PATCH 49/66] Back out 0fc2a36c23d8 (bug 844253) for bustage CLOSED TREE --- js/src/ion/Ion.cpp | 5 +++-- js/src/ion/Ion.h | 3 ++- js/src/jsinterp.cpp | 25 ++++--------------------- js/src/methodjit/InvokeHelpers.cpp | 4 +--- js/src/methodjit/StubCalls.cpp | 3 ++- js/src/vm/Stack-inl.h | 7 ------- js/src/vm/Stack.cpp | 4 ++-- js/src/vm/Stack.h | 24 +++++++----------------- 8 files changed, 21 insertions(+), 54 deletions(-) diff --git a/js/src/ion/Ion.cpp b/js/src/ion/Ion.cpp index bd91164fceee..b5bb7452bc84 100644 --- a/js/src/ion/Ion.cpp +++ b/js/src/ion/Ion.cpp @@ -1472,7 +1472,8 @@ ion::CanEnterAtBranch(JSContext *cx, JSScript *script, AbstractFramePtr fp, } MethodStatus -ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConstructing) +ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, + bool isConstructing, bool newType) { JS_ASSERT(ion::IsEnabled(cx)); @@ -1494,7 +1495,7 @@ ion::CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConst if (isConstructing && fp.thisValue().isPrimitive()) { RootedScript scriptRoot(cx, script); RootedObject callee(cx, &fp.callee()); - RootedObject obj(cx, CreateThisForFunction(cx, callee, fp.useNewType())); + RootedObject obj(cx, CreateThisForFunction(cx, callee, newType)); if (!obj) return Method_Skipped; fp.thisValue().setObject(*obj); diff --git a/js/src/ion/Ion.h b/js/src/ion/Ion.h index c7b069a95cfd..407e09cabb8e 100644 --- a/js/src/ion/Ion.h +++ b/js/src/ion/Ion.h @@ -261,7 +261,8 @@ bool SetIonContext(IonContext *ctx); MethodStatus CanEnterAtBranch(JSContext *cx, JSScript *script, AbstractFramePtr fp, jsbytecode *pc, bool isConstructing); -MethodStatus CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, bool isConstructing); +MethodStatus CanEnter(JSContext *cx, JSScript *script, AbstractFramePtr fp, + bool isConstructing, bool newType); MethodStatus CanEnterUsingFastInvoke(JSContext *cx, HandleScript script, uint32_t numActualArgs); enum IonExecStatus diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index e1a6c2fc8b40..16a27833f51e 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -276,20 +276,6 @@ js::RunScript(JSContext *cx, StackFrame *fp) JS_CHECK_RECURSION(cx, return false); - // Check to see if useNewType flag should be set for this frame. - if (fp->isFunctionFrame() && fp->isConstructing() && !fp->isGeneratorFrame()) { - StackIter iter(cx); - if (!iter.done()) { - ++iter; - if (iter.isScript()) { - RawScript script = iter.script(); - jsbytecode *pc = iter.pc(); - if (UseNewType(cx, script, pc)) - fp->setUseNewType(); - } - } - } - #ifdef DEBUG struct CheckStackBalance { JSContext *cx; @@ -308,7 +294,7 @@ js::RunScript(JSContext *cx, StackFrame *fp) #ifdef JS_ION if (ion::IsEnabled(cx)) { ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(fp), - fp->isConstructing()); + fp->isConstructing(), false); if (status == ion::Method_Error) return false; if (status == ion::Method_Compiled) { @@ -1173,7 +1159,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) if (interpMode == JSINTERP_NORMAL) { StackFrame *fp = regs.fp(); if (!fp->isGeneratorFrame()) { - if (!fp->prologue(cx)) + if (!fp->prologue(cx, UseNewTypeAtEntry(cx, fp))) goto error; } else { Probes::enterScript(cx, script, script->function(), fp); @@ -2375,9 +2361,6 @@ BEGIN_CASE(JSOP_FUNCALL) funScript = fun->nonLazyScript(); if (!cx->stack.pushInlineFrame(cx, regs, args, fun, funScript, initial)) goto error; - - if (newType) - regs.fp()->setUseNewType(); SET_SCRIPT(regs.fp()->script()); #ifdef JS_METHODJIT @@ -2387,7 +2370,7 @@ BEGIN_CASE(JSOP_FUNCALL) #ifdef JS_ION if (!newType && ion::IsEnabled(cx)) { ion::MethodStatus status = ion::CanEnter(cx, script, AbstractFramePtr(regs.fp()), - regs.fp()->isConstructing()); + regs.fp()->isConstructing(), newType); if (status == ion::Method_Error) goto error; if (status == ion::Method_Compiled) { @@ -2422,7 +2405,7 @@ BEGIN_CASE(JSOP_FUNCALL) } #endif - if (!regs.fp()->prologue(cx)) + if (!regs.fp()->prologue(cx, newType)) goto error; if (cx->compartment->debugMode()) { switch (ScriptDebugPrologue(cx, regs.fp())) { diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index 0c1ee81d9b36..01677beaf98e 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -940,9 +940,7 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM return js_InternalThrow(f); fp->initVarsToUndefined(); fp->scopeChain(); - if (types::UseNewTypeAtEntry(cx, fp)) - fp->setUseNewType(); - if (!fp->prologue(cx)) + if (!fp->prologue(cx, types::UseNewTypeAtEntry(cx, fp))) return js_InternalThrow(f); /* diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 496a6a59a159..3f82f21e251f 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -808,7 +808,8 @@ stubs::TriggerIonCompile(VMFrame &f) compileStatus = ion::CanEnterAtBranch(f.cx, script, f.cx->fp(), osrPC, f.fp()->isConstructing()); } else { - compileStatus = ion::CanEnter(f.cx, script, f.cx->fp(), f.fp()->isConstructing()); + compileStatus = ion::CanEnter(f.cx, script, f.cx->fp(), f.fp()->isConstructing(), + /* newType = */ false); } if (compileStatus != ion::Method_Compiled) { diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index ae947ae5ff66..1ca3b328c7c1 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -738,13 +738,6 @@ AbstractFramePtr::hasCallObj() const return false; } inline bool -AbstractFramePtr::useNewType() const -{ - if (isStackFrame()) - return asStackFrame()->useNewType(); - return false; -} -inline bool AbstractFramePtr::isGeneratorFrame() const { if (isStackFrame()) diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index d6ae424492d6..71020d99c335 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -307,7 +307,7 @@ StackFrame::initFunctionScopeObjects(JSContext *cx) } bool -StackFrame::prologue(JSContext *cx) +StackFrame::prologue(JSContext *cx, bool newType) { RootedScript script(cx, this->script()); @@ -339,7 +339,7 @@ StackFrame::prologue(JSContext *cx) if (isConstructing()) { RootedObject callee(cx, &this->callee()); - JSObject *obj = CreateThisForFunction(cx, callee, useNewType()); + JSObject *obj = CreateThisForFunction(cx, callee, newType); if (!obj) return false; functionThis() = ObjectValue(*obj); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 2f37e0909e41..7241bb39739c 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -297,7 +297,6 @@ class AbstractFramePtr inline bool hasArgsObj() const; inline ArgumentsObject &argsObj() const; inline void initArgsObj(ArgumentsObject &argsobj) const; - inline bool useNewType() const; inline bool copyRawFrameSlots(AutoValueVector *vec) const; @@ -390,13 +389,10 @@ class StackFrame HAS_PUSHED_SPS_FRAME = 0x100000, /* SPS was notified of enty */ /* Ion frame state */ - RUNNING_IN_ION = 0x200000, /* frame is running in Ion */ - CALLING_INTO_ION = 0x400000, /* frame is calling into Ion */ + RUNNING_IN_ION = 0x200000, /* frame is running in Ion */ + CALLING_INTO_ION = 0x400000, /* frame is calling into Ion */ - JIT_REVISED_STACK = 0x800000, /* sp was revised by JIT for lowered apply */ - - /* Miscellaneous state. */ - USE_NEW_TYPE = 0x1000000 /* Use new type for constructed |this| object. */ + JIT_REVISED_STACK = 0x800000 /* sp was revised by JIT for lowered apply */ }; private: @@ -493,9 +489,12 @@ class StackFrame * over-recursed) after pushing the stack frame but before 'prologue' is * called or completes fully. To simplify usage, 'epilogue' does not assume * 'prologue' has completed and handles all the intermediate state details. + * + * The 'newType' option indicates whether the constructed 'this' value (if + * there is one) should be given a new singleton type. */ - bool prologue(JSContext *cx); + bool prologue(JSContext *cx, bool newType); void epilogue(JSContext *cx); /* Subsets of 'prologue' called from jit code. */ @@ -1053,15 +1052,6 @@ class StackFrame return flags_ & HAS_ARGS_OBJ; } - void setUseNewType() { - JS_ASSERT(isConstructing()); - flags_ |= USE_NEW_TYPE; - } - bool useNewType() const { - JS_ASSERT(isConstructing()); - return flags_ & USE_NEW_TYPE; - } - /* * The method JIT call/apply optimization can erase Function.{call,apply} * invocations from the stack and push the callee frame directly. The base From ebe9a6d53bc5f04d5fa61cc1df85c9ce7b39e704 Mon Sep 17 00:00:00 2001 From: Gina Yeh Date: Mon, 25 Feb 2013 15:04:38 +0800 Subject: [PATCH 50/66] Bug 844707 - Fix warnings in debug build, r=echou --- dom/bluetooth/BluetoothScoManager.cpp | 2 +- dom/bluetooth/linux/BluetoothDBusService.cpp | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/dom/bluetooth/BluetoothScoManager.cpp b/dom/bluetooth/BluetoothScoManager.cpp index 1291f3ea5121..99fe043ec396 100644 --- a/dom/bluetooth/BluetoothScoManager.cpp +++ b/dom/bluetooth/BluetoothScoManager.cpp @@ -122,7 +122,7 @@ BluetoothScoManager::Init() mSocketStatus = GetConnectionStatus(); sScoObserver = new BluetoothScoManagerObserver(); - if (sScoObserver->Init()) { + if (!sScoObserver->Init()) { NS_WARNING("Cannot set up SCO observers!"); } return true; diff --git a/dom/bluetooth/linux/BluetoothDBusService.cpp b/dom/bluetooth/linux/BluetoothDBusService.cpp index cf5d050a2ee7..aa8938c1961b 100644 --- a/dom/bluetooth/linux/BluetoothDBusService.cpp +++ b/dom/bluetooth/linux/BluetoothDBusService.cpp @@ -2466,11 +2466,9 @@ BluetoothDBusService::Connect(const nsAString& aDeviceAddress, errorStr.AssignLiteral("BluetoothOppManager has connected/is connecting!"); DispatchBluetoothReply(aRunnable, v, errorStr); } + } else { + NS_WARNING("Unknown Profile"); } - -#ifdef DEBUG - NS_WARNING("Unknown Profile"); -#endif } void From ca1bc93d5f8039294a94a3c50fb8fbbd2992c25b Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 24 Feb 2013 23:42:38 -0800 Subject: [PATCH 51/66] Bug 842476: Work around leak resulting from exception that happens during unload by propagating only the prompts we expect and not the later ones. r=jlebar Make this test less sensitive to the timing of its own unloading by passing on only the expected messages and not any messages that occur during unloading. Such messages might cause exceptions (e.g., "'TypeError: can't access dead object' when calling method: [nsIPrompt::alert]"), and those exceptions might in turn trigger leaks in nsXPConnect that last until shutdown because we store the most recent exception from a call to an XPCWrappedJS on the XPCJSRuntime through shutdown (rather than reporting it, as we probably should, and then freeing it), leaking the window. --- .../mochitest/file_browserElement_SetVisibleFrames_Outer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html b/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html index 6432c6dae5c2..b985c01ccc08 100644 --- a/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html +++ b/dom/browser-element/mochitest/file_browserElement_SetVisibleFrames_Outer.html @@ -21,7 +21,7 @@ function handlePrompt(e) { alert('parent:ready'); } - else if (numPrompts > 3) { + else if (numPrompts == 4 || numPrompts == 5) { alert(e.detail.message); } } From d4d58ab8935641477ca95460f4b0af9df66e4497 Mon Sep 17 00:00:00 2001 From: "L. David Baron" Date: Sun, 24 Feb 2013 23:42:38 -0800 Subject: [PATCH 52/66] Bug 404077: Add mochitest support (currently semi-disabled) for making tests fail when an unexpected number of assertions fire. r=ted This adds support for assertion checking in all mochitest suites except for mochitest-browser-chrome. The checking works much like it does in reftest, except for the mechanism for annotating expected assertions, SimpleTest.expectAssertions() (see its in-code documentation). The support is initially disabled in that: (1) It doesn't cause the tests to report failure (and thus turn the tree orange). (2) It prints TEST-DETCEPXENU-FAIL/PASS instead of TEST-UNEXPECTED-FAIL/PASS (so that it doesn't show up in log highlighting). The assertion checking only works within the test runner (which runs multiple tests); it does not function when running only a single test. --- testing/mochitest/jar.mn | 1 + .../mochitest/tests/SimpleTest/Makefile.in | 1 + .../mochitest/tests/SimpleTest/SimpleTest.js | 38 +++++++++++ .../mochitest/tests/SimpleTest/TestRunner.js | 67 +++++++++++++++++-- .../SimpleTest/iframe-between-tests.html | 12 ++++ .../specialpowers/content/specialpowersAPI.js | 4 ++ 6 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 testing/mochitest/tests/SimpleTest/iframe-between-tests.html diff --git a/testing/mochitest/jar.mn b/testing/mochitest/jar.mn index f07903311dfe..742c04d483da 100644 --- a/testing/mochitest/jar.mn +++ b/testing/mochitest/jar.mn @@ -22,6 +22,7 @@ mochikit.jar: content/tests/SimpleTest/SimpleTest.js (tests/SimpleTest/SimpleTest.js) content/tests/SimpleTest/test.css (tests/SimpleTest/test.css) content/tests/SimpleTest/TestRunner.js (tests/SimpleTest/TestRunner.js) + content/tests/SimpleTest/iframe-between-tests.html (tests/SimpleTest/iframe-between-tests.html) content/tests/SimpleTest/WindowSnapshot.js (tests/SimpleTest/WindowSnapshot.js) content/tests/SimpleTest/MockObjects.js (tests/SimpleTest/MockObjects.js) content/tests/SimpleTest/NativeKeyCodes.js (tests/SimpleTest/NativeKeyCodes.js) diff --git a/testing/mochitest/tests/SimpleTest/Makefile.in b/testing/mochitest/tests/SimpleTest/Makefile.in index 50a9530461f9..3d1d974db36c 100644 --- a/testing/mochitest/tests/SimpleTest/Makefile.in +++ b/testing/mochitest/tests/SimpleTest/Makefile.in @@ -21,6 +21,7 @@ _SIMPLETEST_FILES = LogController.js \ MockObjects.js \ NativeKeyCodes.js \ paint_listener.js \ + iframe-between-tests.html \ $(DEPTH)/testing/specialpowers/content/MozillaLogger.js \ $(DEPTH)/docshell/test/chrome/docshell_helpers.js \ $(NULL) diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 933173c114c0..d70009371199 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -468,6 +468,44 @@ SimpleTest.requestLongerTimeout = function (factor) { } } +/** + * Note that the given range of assertions is to be expected. When + * this function is not called, 0 assertions are expected. When only + * one argument is given, that number of assertions are expected. + * + * A test where we expect to have assertions (which should largely be a + * transitional mechanism to get assertion counts down from our current + * situation) can call the SimpleTest.expectAssertions() function, with + * either one or two arguments: one argument gives an exact number + * expected, and two arguments give a range. For example, a test might do + * one of the following: + * + * // Currently triggers two assertions (bug NNNNNN). + * SimpleTest.expectAssertions(2); + * + * // Currently triggers one assertion on Mac (bug NNNNNN). + * if (navigator.platform.indexOf("Mac") == 0) { + * SimpleTest.expectAssertions(1); + * } + * + * // Currently triggers two assertions on all platforms (bug NNNNNN), + * // but intermittently triggers two additional assertions (bug NNNNNN) + * // on Windows. + * if (navigator.platform.indexOf("Win") == 0) { + * SimpleTest.expectAssertions(2, 4); + * } else { + * SimpleTest.expectAssertions(2); + * } + * + * // Intermittently triggers up to three assertions (bug NNNNNN). + * SimpleTest.expectAssertions(0, 3); + */ +SimpleTest.expectAssertions = function(min, max) { + if (parentRunner) { + parentRunner.expectAssertions(min, max); + } +} + SimpleTest.waitForFocus_started = false; SimpleTest.waitForFocus_loaded = false; SimpleTest.waitForFocus_focused = false; diff --git a/testing/mochitest/tests/SimpleTest/TestRunner.js b/testing/mochitest/tests/SimpleTest/TestRunner.js index fe1e980ede44..a624aceaa8dd 100644 --- a/testing/mochitest/tests/SimpleTest/TestRunner.js +++ b/testing/mochitest/tests/SimpleTest/TestRunner.js @@ -72,9 +72,13 @@ function flattenArguments(lst/* ...*/) { var TestRunner = {}; TestRunner.logEnabled = false; TestRunner._currentTest = 0; +TestRunner._lastTestFinished = -1; TestRunner.currentTestURL = ""; TestRunner.originalTestURL = ""; TestRunner._urls = []; +TestRunner._lastAssertionCount = 0; +TestRunner._expectedMinAsserts = 0; +TestRunner._expectedMaxAsserts = 0; TestRunner.timeout = 5 * 60 * 1000; // 5 minutes. TestRunner.maxTimeouts = 4; // halt testing after too many timeouts @@ -146,6 +150,18 @@ TestRunner.requestLongerTimeout = function(factor) { TestRunner.repeat = 0; TestRunner._currentLoop = 0; +TestRunner.expectAssertions = function(min, max) { + if (typeof(max) == "undefined") { + max = min; + } + if (typeof(min) != "number" || typeof(max) != "number" || + min < 0 || max < min) { + throw "bad parameter to expectAssertions"; + } + TestRunner._expectedMinAsserts = min; + TestRunner._expectedMaxAsserts = max; +} + /** * This function is called after generating the summary. **/ @@ -338,6 +354,8 @@ TestRunner.runNextTest = function() { TestRunner._currentTestStartTime = new Date().valueOf(); TestRunner._timeoutFactor = 1; + TestRunner._expectedMinAsserts = 0; + TestRunner._expectedMaxAsserts = 0; TestRunner.log("TEST-START | " + url); // used by automation.py @@ -401,6 +419,16 @@ TestRunner.expectChildProcessCrash = function() { * This stub is called by SimpleTest when a test is finished. **/ TestRunner.testFinished = function(tests) { + // Prevent a test from calling finish() multiple times before we + // have a chance to unload it. + if (TestRunner._currentTest == TestRunner._lastTestFinished) { + TestRunner.error("TEST-UNEXPECTED-FAIL | " + + TestRunner.currentTestURL + + " | called finish() multiple times"); + return; + } + TestRunner._lastTestFinished = TestRunner._currentTest; + function cleanUpCrashDumpFiles() { if (!SpecialPowers.removeExpectedCrashDumpFiles(TestRunner._expectingProcessCrash)) { TestRunner.error("TEST-UNEXPECTED-FAIL | " + @@ -440,12 +468,14 @@ TestRunner.testFinished = function(tests) { " | finished in " + runtime + "ms"); TestRunner.updateUI(tests); - TestRunner._currentTest++; - if (TestRunner.runSlower) { - setTimeout(TestRunner.runNextTest, 1000); + + var interstitialURL; + if ($('testframe').contentWindow.location.protocol == "chrome:") { + interstitialURL = "tests/SimpleTest/iframe-between-tests.html"; } else { - TestRunner.runNextTest(); + interstitialURL = "/tests/SimpleTest/iframe-between-tests.html"; } + TestRunner._makeIframe(interstitialURL, 0); } SpecialPowers.executeAfterFlushingMessageQueue(function() { @@ -454,6 +484,35 @@ TestRunner.testFinished = function(tests) { }); }; +TestRunner.testUnloaded = function() { + if (SpecialPowers.isDebugBuild) { + var newAssertionCount = SpecialPowers.assertionCount(); + var numAsserts = newAssertionCount - TestRunner._lastAssertionCount; + TestRunner._lastAssertionCount = newAssertionCount; + + var url = TestRunner._urls[TestRunner._currentTest]; + var max = TestRunner._expectedMaxAsserts; + var min = TestRunner._expectedMinAsserts; + if (numAsserts > max) { + // WHEN ENABLING, change "log" to "error" and "DETCEPXENU" + // to "UNEXPECTED". + TestRunner.log("TEST-DETCEPXENU-FAIL | " + url + " | Assertion count " + numAsserts + " is greater than expected range " + min + "-" + max + " assertions."); + } else if (numAsserts < min) { + // WHEN ENABLING, change "log" to "error" and "DETCEPXENU" + // to "UNEXPECTED". + TestRunner.log("TEST-DETCEPXENU-PASS | " + url + " | Assertion count " + numAsserts + " is less than expected range " + min + "-" + max + " assertions."); + } else if (numAsserts > 0) { + TestRunner.log("TEST-KNOWN-FAIL | " + url + " | Assertion count " + numAsserts + " within expected range " + min + "-" + max + " assertions."); + } + } + TestRunner._currentTest++; + if (TestRunner.runSlower) { + setTimeout(TestRunner.runNextTest, 1000); + } else { + TestRunner.runNextTest(); + } +}; + /** * Get the results. */ diff --git a/testing/mochitest/tests/SimpleTest/iframe-between-tests.html b/testing/mochitest/tests/SimpleTest/iframe-between-tests.html new file mode 100644 index 000000000000..51e968e58dc5 --- /dev/null +++ b/testing/mochitest/tests/SimpleTest/iframe-between-tests.html @@ -0,0 +1,12 @@ +iframe for between tests + + diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index fa454e73ecd9..09f72dedc549 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -1206,6 +1206,10 @@ SpecialPowersAPI.prototype = { var debug = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2); return this.isDebugBuild = debug.isDebugBuild; }, + assertionCount: function() { + var debugsvc = Cc['@mozilla.org/xpcom/debug;1'].getService(Ci.nsIDebug2); + return debugsvc.assertionCount; + }, /** * Get the message manager associated with an + +
+
+Hello there. hello there. +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/browser/metro/base/tests/text-block.html b/browser/metro/base/tests/text-block.html new file mode 100644 index 000000000000..72123cd263df --- /dev/null +++ b/browser/metro/base/tests/text-block.html @@ -0,0 +1,64 @@ + + + + + + +
+

(start of paragraph) + Alice was beginning to get very (break)
+ tired of sitting by her sister on the bank, and of having nothing to do: once or twice she (span) + (start of span) had peeped into the book her sister was reading (end of span), + but it had no pictures or conversations in it, `and what is the use of a book,' thought Alice + `without pictures or conversation?' (break)
+ (end of paragraph)

+ (in between paragraphs) +

(start of paragraph) + Alice was beginning to get very + tired of sitting by her sister on the bank, and of having nothing to do: once or twice she + had peeped into the book her sister was reading, but it had no pictures or conversations + in it, `and what is the use of a book,' thought Alice `without pictures or conversation?'(break)
+ (end of paragraph)

+ +

(start of paragraph) + So she was considering in her own mind (as well as she could, for the hot day made her + feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth + the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink + eyes ran close by her.
+

+ +

+ There was nothing so VERY remarkable in that; nor did Alice think it so VERY much out of + the way to hear the Rabbit say to itself, `Oh dear! Oh dear! I shall be late!' (when she + thought it over afterwards, it occurred to her that she ought to have wondered at this, + but at the time it all seemed quite natural); but when the Rabbit actually TOOK A WATCH + OUT OF ITS WAISTCOAT- POCKET, and looked at it, and then hurried on, Alice started to her + feet, for it flashed across her mind that she had never before seen a rabbit with either a + waistcoat-pocket, or a watch to take out of it, and burning with curiosity, she ran across + the field after it, and fortunately was just in time to see it pop down a large + rabbit-hole under the hedge.
+
+ In another moment down went Alice after it, never once considering how in the world she + was to get out again.
+
+ The rabbit-hole went straight on like a tunnel for some way, and then dipped suddenly + down, so suddenly that Alice had not a moment to think about stopping herself before she + found herself falling down a very deep well.
+
(start of paragraph) + Either the well was very deep, or she fell very slowly, for she had plenty of time as she + went down to look about her and to wonder what was going to happen next. First, she tried + to look down and make out what she was coming to, but it was too dark to see anything; + then she looked at the sides of the well, and noticed that they were filled with cupboards + and book-shelves; here and there she saw maps and pictures hung upon pegs. She took down a + jar from one of the shelves as she passed; it was labelled `ORANGE MARMALADE', but to her + great disappointment it was empty: she did not like to drop the jar for fear of killing + somebody, so managed to put it into one of the cupboards as she fell past it.
+
+ `Well!' thought Alice to herself, `after such a fall as this, I shall think nothing of + tumbling down stairs! How brave they'll all think me at home! Why, I wouldn't say anything + about it, even if I fell off the top of the house!' (Which was very likely true.)
+
+ Down, down, down. +
+

+ \ No newline at end of file From f97846302ca73d6073dfdb71e7a575ea8cda73a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Jim=C3=A9nez?= Date: Mon, 25 Feb 2013 13:12:44 +0100 Subject: [PATCH 64/66] Bug 809219 - [WebPayment] mozPay unit tests; r=fabrice --- dom/payment/Makefile.in | 2 + dom/payment/tests/Makefile.in | 21 ++ dom/payment/tests/unit/header_helper.js | 38 ++ .../test_paymanager_get_payment_request.js | 348 ++++++++++++++++++ dom/payment/tests/unit/xpcshell.ini | 5 + testing/xpcshell/xpcshell.ini | 1 + 6 files changed, 415 insertions(+) create mode 100644 dom/payment/tests/Makefile.in create mode 100644 dom/payment/tests/unit/header_helper.js create mode 100644 dom/payment/tests/unit/test_paymanager_get_payment_request.js create mode 100644 dom/payment/tests/unit/xpcshell.ini diff --git a/dom/payment/Makefile.in b/dom/payment/Makefile.in index 6b9bafacf277..fa82add18c9c 100644 --- a/dom/payment/Makefile.in +++ b/dom/payment/Makefile.in @@ -22,5 +22,7 @@ EXTRA_JS_MODULES += \ Payment.jsm \ $(NULL) +TEST_DIRS += tests + include $(topsrcdir)/config/config.mk include $(topsrcdir)/config/rules.mk diff --git a/dom/payment/tests/Makefile.in b/dom/payment/tests/Makefile.in new file mode 100644 index 000000000000..e505081782cb --- /dev/null +++ b/dom/payment/tests/Makefile.in @@ -0,0 +1,21 @@ +# 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 + +DIRS = \ + $(NULL) + +MODULE = test_dom_payment + +XPCSHELL_TESTS = unit + +include $(topsrcdir)/config/rules.mk diff --git a/dom/payment/tests/unit/header_helper.js b/dom/payment/tests/unit/header_helper.js new file mode 100644 index 000000000000..4b19b07da1c6 --- /dev/null +++ b/dom/payment/tests/unit/header_helper.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + +let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + +/** + * Start a new payment module. + * + * @param custom_ns + * Namespace with symbols to be injected into the new payment module + * namespace. + * + * @return an object that represents the payment module's namespace. + */ +function newPaymentModule(custom_ns) { + let payment_ns = { + importScripts: function fakeImportScripts() { + Array.slice(arguments).forEach(function (script) { + subscriptLoader.loadSubScript("resource://gre/modules/" + script, this); + }, this); + }, + }; + + // Copy the custom definitions over. + for (let key in custom_ns) { + payment_ns[key] = custom_ns[key]; + } + + // Load the payment module itself. + payment_ns.importScripts("Payment.jsm"); + + return payment_ns; +} diff --git a/dom/payment/tests/unit/test_paymanager_get_payment_request.js b/dom/payment/tests/unit/test_paymanager_get_payment_request.js new file mode 100644 index 000000000000..751e871205eb --- /dev/null +++ b/dom/payment/tests/unit/test_paymanager_get_payment_request.js @@ -0,0 +1,348 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function getPaymentHelper() { + let error; + let paym = newPaymentModule(); + + paym.PaymentManager.paymentFailed = function paymentFailed(aRequestId, + errorMsg) { + error = errorMsg; + }; + + return { + get paymentModule() { + return paym; + }, + get error() { + return error; + } + }; +} + +function run_test() { + run_next_test(); +} + +function testGetPaymentRequest(paymentProviders, test) { + let helper = getPaymentHelper(); + let paym = helper.paymentModule; + + paym.PaymentManager.registeredProviders = paymentProviders; + + let ret = paym.PaymentManager.getPaymentRequestInfo("", test.jwt); + if (!test.result) { + test.ret ? do_check_true(ret) : do_check_false(ret); + } + if (test.error !== null) { + do_check_eq(helper.error, test.error); + } else { + do_check_eq(typeof ret, "object"); + do_check_eq(ret.jwt, test.jwt); + do_check_eq(ret.type, test.result.type); + do_check_eq(ret.providerName, test.result.providerName); + } +} + +add_test(function test_successfull_request() { + let providers = {}; + let type = "mock/payments/inapp/v1"; + providers[type] = { + name: "mockprovider", + description: "Mock Payment Provider", + uri: "https://mockpayprovider.phpfogapp.com/?req=", + requestMethod: "GET" + }; + + // Payload + // { + // "aud": "mockpayprovider.phpfogapp.com", + // "iss": "Enter you app key here!", + // "request": { + // "name": "Piece of Cake", + // "price": "10.50", + // "priceTier": 1, + // "productdata": "transaction_id=86", + // "currencyCode": "USD", + // "description": "Virtual chocolate cake to fill your virtual tummy" + // }, + // "exp": 1352232792, + // "iat": 1352229192, + // "typ": "mock/payments/inapp/v1" + // } + let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" + + "a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" + + "HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" + + "llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" + + "iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" + + "Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" + + "WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" + + "15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" + + "gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" + + "IyKIC1TKelVhNklvk-Ou1l_daKntaFI"; + + testGetPaymentRequest(providers, { + jwt: jwt, + ret: true, + error: null, + result: { + type: type, + providerName: providers[type].name + } + }); + + run_next_test(); +}); + +add_test(function test_successfull_request_html_description() { + let providers = {}; + let type = "mozilla/payments/pay/v1"; + providers[type] = { + name: "webpay", + description: "Mozilla Payment Provider", + uri: "https://marketplace.firefox.com/mozpay/?req=", + requestMethod: "GET" + }; + + // Payload + // { + // "aud": "marketplace.firefox.com", + // "iss": "marketplace-dev.allizom.org", + // "request": { + // "name": "Krupa's paid app 1", + // "chargebackURL": "http://localhost:8002/telefonica/services/webpay/" + // "chargeback", + // "postbackURL": "http://localhost:8002/telefonica/services/webpay/" + // "postback", + // "productData": "addon_id=85&seller_uuid=d4855df9-6ce0-45cd-81cb-" + // "cf8737e1e7aa&contrib_uuid=201868b7ac2cda410a99b3" + // "ed4c11a8ea", + // "pricePoint": 1, + // "id": "maude:85", + // "description": "This app has been automatically generated by testmanifest.com" + // "" + // }, + // "exp": 1358379147, + // "iat": 1358375547, + // "typ": "mozilla/payments/pay/v1" + // } + let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibWFya2V0cGx" + + "hY2UuZmlyZWZveC5jb20iLCAiaXNzIjogIm1hcmtldHBsYWNlLWRldi5hbGxpem9" + + "tLm9yZyIsICJyZXF1ZXN0IjogeyJuYW1lIjogIktydXBhJ3MgcGFpZCBhcHAgMSI" + + "sICJjaGFyZ2ViYWNrVVJMIjogImh0dHA6Ly9sb2NhbGhvc3Q6ODAwMi90ZWxlZm9" + + "uaWNhL3NlcnZpY2VzL3dlYnBheS9jaGFyZ2ViYWNrIiwgInBvc3RiYWNrVVJMIjo" + + "gImh0dHA6Ly9sb2NhbGhvc3Q6ODAwMi90ZWxlZm9uaWNhL3NlcnZpY2VzL3dlYnB" + + "heS9wb3N0YmFjayIsICJwcm9kdWN0RGF0YSI6ICJhZGRvbl9pZD04NSZzZWxsZXJ" + + "fdXVpZD1kNDg1NWRmOS02Y2UwLTQ1Y2QtODFjYi1jZjg3MzdlMWU3YWEmY29udHJ" + + "pYl91dWlkPTIwMTg2OGI3YWMyY2RhNDEwYTk5YjNlZDRjMTFhOGVhIiwgInByaWN" + + "lUG9pbnQiOiAxLCAiaWQiOiAibWF1ZGU6ODUiLCAiZGVzY3JpcHRpb24iOiAiVGh" + + "pcyBhcHAgaGFzIGJlZW4gYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgYnkgPGEgaHJ" + + "lZj1cImh0dHA6Ly9vdXRnb2luZy5tb3ppbGxhLm9yZy92MS9iYTdmMzczYWUxNjc" + + "4OWVmZjNhYmZkOTVjYThkM2MxNWQxOGRjOTAwOWFmYTIwNGRjNDNmODVhNTViMWY" + + "2ZWYxL2h0dHAlM0EvL3Rlc3RtYW5pZmVzdC5jb21cIiByZWw9XCJub2ZvbGxvd1w" + + "iPnRlc3RtYW5pZmVzdC5jb208L2E-In0sICJleHAiOiAxMzU4Mzc5MTQ3LCAiaWF" + + "0IjogMTM1ODM3NTU0NywgInR5cCI6ICJtb3ppbGxhL3BheW1lbnRzL3BheS92MSJ" + + "9.kgSt636OSRBezMGtm9QLeDxlEOevL4xcOoDj8VRJyD8"; + + testGetPaymentRequest(providers, { + jwt: jwt, + ret: true, + error: null, + result: { + type: type, + providerName: providers[type].name + } + }); + + run_next_test(); +}); + +add_test(function test_empty_jwt() { + testGetPaymentRequest(null, { + jwt: "", + ret: true, + error: "INTERNAL_ERROR_CALL_WITH_MISSING_JWT" + }); + + run_next_test(); +}); + +add_test(function test_wrong_segments_count() { + // 1 segment JWT + let OneSegJwt = "eyJhbGciOiJIUzI1NiJ9"; + testGetPaymentRequest(null, { + jwt: OneSegJwt, + ret: true, + error: "PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT" + }); + + // 2 segments JWT + let TwoSegJwt = "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIwNTg2NDkwMTM2NTY2N" + + "zU1ODY2MSIsImF1ZCI6Ikdvb2dsZSIsInR5cCI6Imdvb2dsZS9" + + "wYXltZW50cy9pbmFwcC9pdGVtL3YxIiwiaWF0IjoxMzUyMjIwM" + + "jEyLCJleHAiOjEzNTIzMDY2MTIsInJlcXVlc3QiOnsiY3VycmV" + + "uY3lDb2RlIjoiVVNEIiwicHJpY2UiOiIzLjAwIiwibmFtZSI6I" + + "kdvbGQgU3RhciIsInNlbGxlckRhdGEiOiJzb21lIG9wYXF1ZSB" + + "kYXRhIiwiZGVzY3JpcHRpb24iOiJBIHNoaW5pbmcgYmFkZ2Ugb" + + "2YgZGlzdGluY3Rpb24ifX0"; + + testGetPaymentRequest(null, { + jwt: TwoSegJwt, + ret: true, + error: "PAY_REQUEST_ERROR_WRONG_SEGMENTS_COUNT" + }); + + run_next_test(); +}); + +add_test(function test_empty_payload() { + let EmptyPayloadJwt = "eyJhbGciOiJIUzI1NiJ9..eyJpc3MiOiIwNTg2NDkwMTM2NTY2N"; + + testGetPaymentRequest(null, { + jwt: EmptyPayloadJwt, + ret: true, + error: "PAY_REQUEST_ERROR_EMPTY_PAYLOAD" + }); + + run_next_test(); +}); + +add_test(function test_missing_typ_parameter() { + // Payload + // { + // "iss": "640ae477-df33-45cd-83b8-6f1f910a6494", + // "iat": 1361203745, + // "request": { + // "description": "detailed description", + // "id": "799db970-7afa-4028-bdb7-8b045eb8babc", + // "postbackURL": "http://inapp-pay-test.farmdev.com/postback", + // "productData": "transaction_id=58", + // "pricePoint": 1, + // "chargebackURL": "http://inapp-pay-test.farmdev.com/chargeback", + // "name": "The Product" + // }, + // "aud": "marketplace-dev.allizom.org", + // "exp": 1361207345 + // } + let missingTypJwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9." + + "eyJpc3MiOiAiNjQwYWU0NzctZGYzMy00NWNkLTgzY" + + "jgtNmYxZjkxMGE2NDk0IiwgImlhdCI6IDEzNjEyMD" + + "M3NDUsICJyZXF1ZXN0IjogeyJkZXNjcmlwdGlvbiI" + + "6ICJkZXRhaWxlZCBkZXNjcmlwdGlvbiIsICJpZCI6" + + "ICI3OTlkYjk3MC03YWZhLTQwMjgtYmRiNy04YjA0N" + + "WViOGJhYmMiLCAicG9zdGJhY2tVUkwiOiAiaHR0cD" + + "ovL2luYXBwLXBheS10ZXN0LmZhcm1kZXYuY29tL3B" + + "vc3RiYWNrIiwgInByb2R1Y3REYXRhIjogInRyYW5z" + + "YWN0aW9uX2lkPTU4IiwgInByaWNlUG9pbnQiOiAxL" + + "CAiY2hhcmdlYmFja1VSTCI6ICJodHRwOi8vaW5hcH" + + "AtcGF5LXRlc3QuZmFybWRldi5jb20vY2hhcmdlYmF" + + "jayIsICJuYW1lIjogIlRoZSBQcm9kdWN0In0sICJh" + + "dWQiOiAibWFya2V0cGxhY2UtZGV2LmFsbGl6b20ub" + + "3JnIiwgImV4cCI6IDEzNjEyMDczNDV9.KAHsJX1Hy" + + "fmwNvAckdVUqlpPvdHggpx9yX276TWacRg"; + testGetPaymentRequest(null, { + jwt: missingTypJwt, + ret: true, + error: "PAY_REQUEST_ERROR_NO_TYP_PARAMETER" + }); + + run_next_test(); +}); + +add_test(function test_missing_request_parameter() { + // Payload + // { + // "iss": "Enter you app key here!", + // "iat": 1352225299, + // "typ": "mock/payments/inapp/v1", + // "aud": "mockpayprovider.phpfogapp.com", + // "exp": 1352228899 + // } + let missingRequestJwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9." + + "eyJpc3MiOiAiRW50ZXIgeW91IGFwcCBrZXkgaGVyZ" + + "SEiLCAiaWF0IjogMTM1MjIyNTI5OSwgInR5cCI6IC" + + "Jtb2NrL3BheW1lbnRzL2luYXBwL3YxIiwgImF1ZCI" + + "6ICJtb2NrcGF5cHJvdmlkZXIucGhwZm9nYXBwLmNv" + + "bSIsICJleHAiOiAxMzUyMjI4ODk5fQ.yXGinvZiUs" + + "v9JWvdfM6zPD0iOX9DgCPcIwIbCrL4tcs"; + + testGetPaymentRequest(null, { + jwt: missingRequestJwt, + ret: true, + error: "PAY_REQUEST_ERROR_NO_REQUEST_PARAMETER" + }); + + run_next_test(); +}); + +add_test(function test_jwt_decoding_error() { + let wrongJwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.^eyJhdWQiOiAibW9" + + "a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" + + "HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" + + "llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" + + "iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" + + "Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" + + "WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" + + "15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" + + "gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" + + "IyKIC1TKelVhNklvk-Ou1l_daKntaFI"; + + testGetPaymentRequest(null, { + jwt: wrongJwt, + ret: true, + error: "PAY_REQUEST_ERROR_ERROR_DECODING_JWT" + }); + + run_next_test(); +}); + +add_test(function test_non_https_provider() { + let providers = {}; + let type = "mock/payments/inapp/v1"; + providers[type] = { + name: "mockprovider", + description: "Mock Payment Provider", + uri: "http://mockpayprovider.phpfogapp.com/?req=", + requestMethod: "GET" + }; + + // Payload + // { + // "aud": "mockpayprovider.phpfogapp.com", + // "iss": "Enter you app key here!", + // "request": { + // "name": "Piece of Cake", + // "price": "10.50", + // "priceTier": 1, + // "productdata": "transaction_id=86", + // "currencyCode": "USD", + // "description": "Virtual chocolate cake to fill your virtual tummy" + // }, + // "exp": 1352232792, + // "iat": 1352229192, + // "typ": "mock/payments/inapp/v1" + // } + let jwt = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJhdWQiOiAibW9j" + + "a3BheXByb3ZpZGVyLnBocGZvZ2FwcC5jb20iLCAiaXNzIjogIkVudGVyI" + + "HlvdSBhcHAga2V5IGhlcmUhIiwgInJlcXVlc3QiOiB7Im5hbWUiOiAiUG" + + "llY2Ugb2YgQ2FrZSIsICJwcmljZSI6ICIxMC41MCIsICJwcmljZVRpZXI" + + "iOiAxLCAicHJvZHVjdGRhdGEiOiAidHJhbnNhY3Rpb25faWQ9ODYiLCAi" + + "Y3VycmVuY3lDb2RlIjogIlVTRCIsICJkZXNjcmlwdGlvbiI6ICJWaXJ0d" + + "WFsIGNob2NvbGF0ZSBjYWtlIHRvIGZpbGwgeW91ciB2aXJ0dWFsIHR1bW" + + "15In0sICJleHAiOiAxMzUyMjMyNzkyLCAiaWF0IjogMTM1MjIyOTE5Miw" + + "gInR5cCI6ICJtb2NrL3BheW1lbnRzL2luYXBwL3YxIn0.QZxc62USCy4U" + + "IyKIC1TKelVhNklvk-Ou1l_daKntaFI"; + + testGetPaymentRequest(providers, { + jwt: jwt, + ret: true, + error: "INTERNAL_ERROR_NON_HTTPS_PROVIDER_URI" + }); + + run_next_test(); +}); diff --git a/dom/payment/tests/unit/xpcshell.ini b/dom/payment/tests/unit/xpcshell.ini new file mode 100644 index 000000000000..0d3a52ed3b1f --- /dev/null +++ b/dom/payment/tests/unit/xpcshell.ini @@ -0,0 +1,5 @@ +[DEFAULT] +head = header_helper.js +tail = + +[test_paymanager_get_payment_request.js] diff --git a/testing/xpcshell/xpcshell.ini b/testing/xpcshell/xpcshell.ini index 723fde31ef83..271f966a7693 100644 --- a/testing/xpcshell/xpcshell.ini +++ b/testing/xpcshell/xpcshell.ini @@ -17,6 +17,7 @@ [include:dom/network/tests/unit/xpcshell.ini] [include:dom/network/tests/unit_ipc/xpcshell.ini] [include:dom/network/tests/unit_stats/xpcshell.ini] +[include:dom/payment/tests/unit/xpcshell.ini] [include:dom/permission/tests/unit/xpcshell.ini] [include:dom/src/json/test/unit/xpcshell.ini] [include:dom/system/gonk/tests/xpcshell.ini] From 2defe9ec0b3f59ecc223eb4758d261ef15479e3c Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 22 Feb 2013 13:49:05 -0500 Subject: [PATCH 65/66] Bug 844188 - use rcs.mk's functions for getting the source repo for telemetry revision ids; r=glandium --- toolkit/components/telemetry/Makefile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/Makefile.in b/toolkit/components/telemetry/Makefile.in index bf3b9e1273bb..199d39b2d4fc 100644 --- a/toolkit/components/telemetry/Makefile.in +++ b/toolkit/components/telemetry/Makefile.in @@ -9,6 +9,8 @@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk +USE_RCS_MK := 1 +include $(topsrcdir)/config/makefiles/rcs.mk MODULE = telemetry XPIDL_MODULE = telemetry @@ -63,7 +65,7 @@ ifdef MOZILLA_OFFICIAL DEFINES += -DMOZILLA_OFFICIAL endif -MOZ_HISTOGRAMS_VERSION ?= $(shell hg path default)rev/$(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null) +MOZ_HISTOGRAMS_VERSION ?= $(call getSourceRepo)/rev/$(firstword $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null)) ifdef MOZ_HISTOGRAMS_VERSION DEFINES += -DHISTOGRAMS_FILE_VERSION="$(MOZ_HISTOGRAMS_VERSION)" endif From 6598183003b036c31c3be84ad408556ac5ca8144 Mon Sep 17 00:00:00 2001 From: "Nicholas D. Matsakis" Date: Mon, 25 Feb 2013 08:06:48 -0500 Subject: [PATCH 66/66] Bug 843684 - Patch up include to avoid compilation warnings when JS_THREADSAFE is not defined r=njn --- js/src/vm/ParallelDo.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/vm/ParallelDo.cpp b/js/src/vm/ParallelDo.cpp index dc3715e7202b..31a5433c0f14 100644 --- a/js/src/vm/ParallelDo.cpp +++ b/js/src/vm/ParallelDo.cpp @@ -19,9 +19,9 @@ #include "jsinterpinlines.h" #include "jsobjinlines.h" -#if defined(JS_THREADSAFE) && defined(JS_ION) +#ifdef JS_ION #include "ion/ParallelArrayAnalysis.h" -#endif // THREADSAFE && ION +#endif // ION #if defined(DEBUG) && defined(JS_THREADSAFE) && defined(JS_ION) #include "ion/Ion.h"