author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Tue, 08 Sep 2015 15:37:12 +0200 | |
changeset 261308 | b23b2fa33a9dcda59dbbca1d157eca3c32c5b862 |
parent 261307 | bc2b0fbde72c40da54e0718d52eaf080549a7070 (current diff) |
parent 261233 | 2bb231870f2d4ac66fea59f40e8d3e893816803c (diff) |
child 261309 | 7fa38a96266144196e943333cacfec791deb6da8 |
child 261364 | 20cc75c647f1d3f8b4bc594b989f4cd0e73c9949 |
child 261381 | c832cffd0ecc7e1f817a81655ba60244f266491d |
push id | 64705 |
push user | cbook@mozilla.com |
push date | Tue, 08 Sep 2015 14:02:43 +0000 |
treeherder | mozilla-inbound@7fa38a962661 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 43.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
dom/media/DecodedStream.cpp | file | annotate | diff | comparison | revisions | |
dom/media/DecodedStream.h | file | annotate | diff | comparison | revisions | |
js/src/jit/mips32/AtomicOperations-mips32.h | file | annotate | diff | comparison | revisions | |
toolkit/system/dbus/moz.build | file | annotate | diff | comparison | revisions | |
toolkit/system/dbus/nsDBusModule.cpp | file | annotate | diff | comparison | revisions | |
toolkit/system/dbus/nsDBusService.cpp | file | annotate | diff | comparison | revisions | |
toolkit/system/dbus/nsDBusService.h | file | annotate | diff | comparison | revisions | |
toolkit/system/dbus/nsNetworkManagerListener.cpp | file | annotate | diff | comparison | revisions | |
toolkit/system/dbus/nsNetworkManagerListener.h | file | annotate | diff | comparison | revisions |
--- a/.hgignore +++ b/.hgignore @@ -1,16 +1,17 @@ # .hgignore - List of filenames hg should ignore # Filenames that should be ignored wherever they appear ~$ \.py(c|o)$ (?i)(^|/)TAGS$ (^|/)ID$ (^|/)\.DS_Store$ +.*\.egg-info # Vim swap files. ^\.sw.$ .[^/]*\.sw.$ # Emacs directory variable files. \.dir-locals\.el
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py +++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py @@ -53,22 +53,22 @@ DEFAULT_NO_CONNECTIONS_PREFS = { 'media.gmp-manager.cert.requireBuiltIn' : False, 'media.gmp-manager.url' : 'http://localhost/media-dummy/gmpmanager', 'media.gmp-manager.url.override': 'http://localhost/dummy-gmp-manager.xml', 'browser.aboutHomeSnippets.updateUrl': 'https://localhost/snippet-dummy', 'browser.newtab.url' : 'about:blank', 'browser.search.update': False, 'browser.search.suggest.enabled' : False, 'browser.safebrowsing.enabled' : False, - 'browser.safebrowsing.updateURL': 'http://localhost/safebrowsing-dummy/update', - 'browser.safebrowsing.gethashURL': 'http://localhost/safebrowsing-dummy/gethash', + 'browser.safebrowsing.provider.google.updateURL': 'http://localhost/safebrowsing-dummy/update', + 'browser.safebrowsing.provider.google.gethashURL': 'http://localhost/safebrowsing-dummy/gethash', 'browser.safebrowsing.malware.reportURL': 'http://localhost/safebrowsing-dummy/malwarereport', 'browser.selfsupport.url': 'https://localhost/selfsupport-dummy', - 'browser.trackingprotection.gethashURL': 'http://localhost/safebrowsing-dummy/gethash', - 'browser.trackingprotection.updateURL': 'http://localhost/safebrowsing-dummy/update', + 'browser.safebrowsing.provider.mozilla.gethashURL': 'http://localhost/safebrowsing-dummy/gethash', + 'browser.safebrowsing.provider.mozilla.updateURL': 'http://localhost/safebrowsing-dummy/update', # Disable app update 'app.update.enabled' : False, 'app.update.staging.enabled': False, # Disable about:newtab content fetch and ping 'browser.newtabpage.directory.source': 'data:application/json,{"jetpack":1}', 'browser.newtabpage.directory.ping': '', @@ -115,18 +115,20 @@ DEFAULT_FIREFOX_PREFS = { 'devtools.errorconsole.enabled' : True, 'devtools.chrome.enabled' : True, # From: # http://hg.mozilla.org/mozilla-central/file/1dd81c324ac7/build/automation.py.in#l388 # Make url-classifier updates so rare that they won't affect tests. 'urlclassifier.updateinterval' : 172800, # Point the url-classifier to a nonexistent local URL for fast failures. - 'browser.safebrowsing.provider.0.gethashURL' : 'http://localhost/safebrowsing-dummy/gethash', - 'browser.safebrowsing.provider.0.updateURL' : 'http://localhost/safebrowsing-dummy/update', + 'browser.safebrowsing.provider.google.gethashURL' : 'http://localhost/safebrowsing-dummy/gethash', + 'browser.safebrowsing.provider.google.updateURL' : 'http://localhost/safebrowsing-dummy/update', + 'browser.safebrowsing.provider.mozilla.gethashURL': 'http://localhost/safebrowsing-dummy/gethash', + 'browser.safebrowsing.provider.mozilla.updateURL': 'http://localhost/safebrowsing-dummy/update', } # When launching a temporary new Thunderbird profile, use these preferences. # Note that these were taken from: # http://mxr.mozilla.org/comm-central/source/mail/test/mozmill/runtest.py DEFAULT_THUNDERBIRD_PREFS = { # say no to slow script warnings 'dom.max_chrome_script_run_time': 200,
--- a/addon-sdk/source/test/preferences/firefox.json +++ b/addon-sdk/source/test/preferences/firefox.json @@ -1,10 +1,12 @@ { "browser.startup.homepage": "about:blank", "startup.homepage_welcome_url": "about:blank", "devtools.browsertoolbox.panel": "jsdebugger", "devtools.errorconsole.enabled": true, "devtools.chrome.enabled": true, "urlclassifier.updateinterval": 172800, - "browser.safebrowsing.provider.0.gethashURL": "http://localhost/safebrowsing-dummy/gethash", - "browser.safebrowsing.provider.0.updateURL": "http://localhost/safebrowsing-dummy/update" + "browser.safebrowsing.provider.google.gethashURL": "http://localhost/safebrowsing-dummy/gethash", + "browser.safebrowsing.provider.google.updateURL": "http://localhost/safebrowsing-dummy/update", + "browser.safebrowsing.provider.mozilla.gethashURL": "http://localhost/safebrowsing-dummy/gethash", + "browser.safebrowsing.provider.mozilla.updateURL": "http://localhost/safebrowsing-dummy/update" }
--- a/addon-sdk/source/test/preferences/no-connections.json +++ b/addon-sdk/source/test/preferences/no-connections.json @@ -10,22 +10,22 @@ "media.gmp-manager.cert.requireBuiltIn": false, "media.gmp-manager.url": "http://localhost/media-dummy/gmpmanager", "media.gmp-manager.url.override": "http://localhost/dummy-gmp-manager.xml", "browser.aboutHomeSnippets.updateUrl": "https://localhost/snippet-dummy", "browser.newtab.url": "about:blank", "browser.search.update": false, "browser.search.suggest.enabled": false, "browser.safebrowsing.enabled": false, - "browser.safebrowsing.updateURL": "http://localhost/safebrowsing-dummy/update", - "browser.safebrowsing.gethashURL": "http://localhost/safebrowsing-dummy/gethash", - "browser.safebrowsing.malware.reportURL": "http://localhost/safebrowsing-dummy/malwarereport", + "browser.safebrowsing.provider.google.updateURL": "http://localhost/safebrowsing-dummy/update", + "browser.safebrowsing.provider.google.gethashURL": "http://localhost/safebrowsing-dummy/gethash", + "browser.safebrowsing.provider.google.reportURL": "http://localhost/safebrowsing-dummy/malwarereport", "browser.selfsupport.url": "https://localhost/selfsupport-dummy", - "browser.trackingprotection.gethashURL": "http://localhost/safebrowsing-dummy/gethash", - "browser.trackingprotection.updateURL": "http://localhost/safebrowsing-dummy/update", + "browser.safebrowsing.provider.mozilla.gethashURL": "http://localhost/safebrowsing-dummy/gethash", + "browser.safebrowsing.provider.mozilla.updateURL": "http://localhost/safebrowsing-dummy/update", "browser.newtabpage.directory.source": "data:application/json,{'jetpack':1}", "browser.newtabpage.directory.ping": "", "extensions.update.url": "http://localhost/extensions-dummy/updateURL", "extensions.update.background.url": "http://localhost/extensions-dummy/updateBackgroundURL", "extensions.blocklist.url": "http://localhost/extensions-dummy/blocklistURL", "extensions.webservice.discoverURL": "http://localhost/extensions-dummy/discoveryURL", "extensions.getAddons.maxResults": 0, "geo.wifi.uri": "http://localhost/location-dummy/locationURL",
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -351,68 +351,50 @@ pref("image.onload.decode.limit", 24); / // XXX this isn't a good check for "are touch events supported", but // we don't really have a better one at the moment. // enable touch events interfaces pref("dom.w3c_touch_events.enabled", 1); pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240" pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240" #ifdef MOZ_SAFE_BROWSING -// Safe browsing does nothing unless this pref is set pref("browser.safebrowsing.enabled", false); - // Prevent loading of pages identified as malware pref("browser.safebrowsing.malware.enabled", false); - +pref("browser.safebrowsing.downloads.enabled", false); +pref("browser.safebrowsing.downloads.remote.enabled", false); +pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); pref("browser.safebrowsing.debug", false); -pref("browser.safebrowsing.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); + +pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); +pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); +pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); +pref("browser.safebrowsing.provider.google.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); + pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); pref("browser.safebrowsing.id", "Firefox"); // Tables for application reputation. pref("urlclassifier.downloadBlockTable", "goog-badbinurl-shavar"); -// Non-enhanced mode (local url lists) URL list to check for updates -pref("browser.safebrowsing.provider.0.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client={moz:client}&appver={moz:version}&pver=2.2&key=%GOOGLE_API_KEY%"); - -pref("browser.safebrowsing.dataProvider", 0); - -// Does the provider name need to be localizable? -pref("browser.safebrowsing.provider.0.name", "Google"); -pref("browser.safebrowsing.provider.0.reportURL", "https://safebrowsing.google.com/safebrowsing/report?"); -pref("browser.safebrowsing.provider.0.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client={moz:client}&appver={moz:version}&pver=2.2"); - -// HTML report pages -pref("browser.safebrowsing.provider.0.reportGenericURL", "http://{moz:locale}.phish-generic.mozilla.com/?hl={moz:locale}"); -pref("browser.safebrowsing.provider.0.reportErrorURL", "http://{moz:locale}.phish-error.mozilla.com/?hl={moz:locale}"); -pref("browser.safebrowsing.provider.0.reportPhishURL", "http://{moz:locale}.phish-report.mozilla.com/?hl={moz:locale}"); -pref("browser.safebrowsing.provider.0.reportMalwareURL", "http://{moz:locale}.malware-report.mozilla.com/?hl={moz:locale}"); -pref("browser.safebrowsing.provider.0.reportMalwareErrorURL", "http://{moz:locale}.malware-error.mozilla.com/?hl={moz:locale}"); - -// FAQ URLs - // The number of random entries to send with a gethash request. pref("urlclassifier.gethashnoise", 4); // Gethash timeout for Safebrowsing. pref("urlclassifier.gethash.timeout_ms", 5000); // If an urlclassifier table has not been updated in this number of seconds, // a gethash request will be forced to check that the result is still in // the database. pref("urlclassifier.max-complete-age", 2700); -// URL for checking the reason for a malware warning. -pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); - // Tracking protection pref("privacy.trackingprotection.enabled", true); pref("privacy.trackingprotection.pbmode.enabled", false); #endif // True if this is the first time we are showing about:firstrun pref("browser.firstrun.show.uidiscovery", true);
--- a/b2g/components/FxAccountsMgmtService.jsm +++ b/b2g/components/FxAccountsMgmtService.jsm @@ -88,22 +88,33 @@ this.FxAccountsMgmtService = { return; } // Backwards compatibility: handle accountId coming from Gaia if (data.accountId && typeof(data.email === "undefined")) { data.email = data.accountId; delete data.accountId; } - // XXX dirty hack because Gaia is sending getAccounts. + // Bug 1202450 dirty hack because Gaia is sending getAccounts. if (data.method == "getAccounts") { data.method = "getAccount"; } switch(data.method) { + case "getAssertion": + let principal = Services.scriptSecurityManager.getSystemPrincipal(); + let audience = msg.audience || principal.originNoSuffix; + FxAccountsManager.getAssertion(audience, principal, { + silent: msg.silent || false + }).then(result => { + self._onFulfill(msg.id, result); + }, reason => { + self._onReject(msg.id, reason); + }); + break; case "getAccount": case "getKeys": FxAccountsManager[data.method]().then( result => { // For the getAccounts case, we only expose the email and // verification status so far. self._onFulfill(msg.id, result); },
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -952,25 +952,25 @@ pref("gecko.handlerService.allowRegister #ifdef MOZ_SAFE_BROWSING pref("browser.safebrowsing.enabled", true); pref("browser.safebrowsing.malware.enabled", true); pref("browser.safebrowsing.downloads.enabled", true); pref("browser.safebrowsing.downloads.remote.enabled", true); pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); pref("browser.safebrowsing.debug", false); -pref("browser.safebrowsing.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); +pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); +pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); +pref("browser.safebrowsing.provider.google.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); + pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); - -pref("browser.safebrowsing.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); - #ifdef MOZILLA_OFFICIAL // Normally the "client ID" sent in updates is appinfo.name, but for // official Firefox releases from Mozilla we use a special identifier. pref("browser.safebrowsing.id", "navclient-auto-ffox"); #endif // Name of the about: page contributed by safebrowsing to handle display of error // pages on phishing/malware hits. (bug 399233)
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -491,19 +491,16 @@ @RESPATH@/browser/components/360seProfileMigrator.js @RESPATH@/browser/components/EdgeProfileMigrator.js @RESPATH@/browser/components/IEProfileMigrator.js @RESPATH@/browser/components/SafariProfileMigrator.js #endif #ifdef XP_MACOSX @RESPATH@/browser/components/SafariProfileMigrator.js #endif -#ifdef MOZ_ENABLE_DBUS -@RESPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@ -#endif #ifdef MOZ_ENABLE_GNOME_COMPONENT @RESPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@ #endif #if defined(MOZ_ENABLE_DBUS) || defined(MOZ_ENABLE_GNOME_COMPONENT) @RESPATH@/components/components.manifest #endif @RESPATH@/components/nsINIProcessor.manifest @RESPATH@/components/nsINIProcessor.js
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2400,17 +2400,17 @@ Element::SetAttrAndNotify(int32_t aNames // Don't pass aOldValue to AttributeChanged since it may not be reliable. // Callers only compute aOldValue under certain conditions which may not // be triggered by all nsIMutationObservers. nsNodeUtils::AttributeChanged(this, aNamespaceID, aName, aModType, oldValue == &aParsedValue ? &aParsedValue : nullptr); } if (aFireMutation) { - InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED); + InternalMutationEvent mutation(true, eLegacyAttrModified); nsAutoString ns; nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns); Attr* attrNode = GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName)); mutation.mRelatedNode = attrNode; mutation.mAttrName = aName; @@ -2642,17 +2642,17 @@ Element::UnsetAttr(int32_t aNameSpaceID, rv = AfterSetAttr(aNameSpaceID, aName, nullptr, aNotify); NS_ENSURE_SUCCESS(rv, rv); if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) { OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify); } if (hasMutationListeners) { - InternalMutationEvent mutation(true, NS_MUTATION_ATTRMODIFIED); + InternalMutationEvent mutation(true, eLegacyAttrModified); mutation.mRelatedNode = attrNode; mutation.mAttrName = aName; nsAutoString value; oldValue.ToString(value); if (!value.IsEmpty()) mutation.mPrevAttrValue = do_GetAtom(value);
--- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -1212,17 +1212,17 @@ FragmentOrElement::FireNodeInserted(nsID nsTArray<nsCOMPtr<nsIContent> >& aNodes) { uint32_t count = aNodes.Length(); for (uint32_t i = 0; i < count; ++i) { nsIContent* childContent = aNodes[i]; if (nsContentUtils::HasMutationListeners(childContent, NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) { - InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED); + InternalMutationEvent mutation(true, eLegacyNodeInserted); mutation.mRelatedNode = do_QueryInterface(aParent); mozAutoSubtreeModified subtree(aDoc, aParent); (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe(); } } }
--- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -4080,17 +4080,17 @@ nsContentUtils::MaybeFireNodeRemoved(nsI NS_ERROR("Want to fire DOMNodeRemoved event, but it's not safe"); WarnScriptWasIgnored(aOwnerDoc); } return; } if (HasMutationListeners(aChild, NS_EVENT_BITS_MUTATION_NODEREMOVED, aParent)) { - InternalMutationEvent mutation(true, NS_MUTATION_NODEREMOVED); + InternalMutationEvent mutation(true, eLegacyNodeRemoved); mutation.mRelatedNode = do_QueryInterface(aParent); mozAutoSubtreeModified subtree(aOwnerDoc, aParent); EventDispatcher::Dispatch(aChild, nullptr, &mutation); } } void
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -9431,17 +9431,17 @@ nsDocument::MutationEventDispatched(nsIN realTargets.AppendObject(possibleTarget); } } mSubtreeModifiedTargets.Clear(); int32_t realTargetCount = realTargets.Count(); for (int32_t k = 0; k < realTargetCount; ++k) { - InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED); + InternalMutationEvent mutation(true, eLegacySubtreeModified); (new AsyncEventDispatcher(realTargets[k], mutation))-> RunDOMEventWhenSafe(); } } } void nsDocument::AddStyleRelevantLink(Link* aLink)
--- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -382,17 +382,17 @@ nsGenericDOMDataNode::SetTextInternal(ui aOffset, endOffset, aLength, aDetails }; nsNodeUtils::CharacterDataChanged(this, &info); if (haveMutationListeners) { - InternalMutationEvent mutation(true, NS_MUTATION_CHARACTERDATAMODIFIED); + InternalMutationEvent mutation(true, eLegacyCharacterDataModified); mutation.mPrevAttrValue = oldValue; if (aLength > 0) { nsAutoString val; mText.AppendTo(val); mutation.mNewAttrValue = do_GetAtom(val); }
--- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1605,17 +1605,17 @@ nsINode::doInsertChildAt(nsIContent* aKi if (parent && isAppend) { nsNodeUtils::ContentAppended(parent, aKid, aIndex); } else { nsNodeUtils::ContentInserted(this, aKid, aIndex); } if (nsContentUtils::HasMutationListeners(aKid, NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) { - InternalMutationEvent mutation(true, NS_MUTATION_NODEINSERTED); + InternalMutationEvent mutation(true, eLegacyNodeInserted); mutation.mRelatedNode = do_QueryInterface(this); mozAutoSubtreeModified subtree(OwnerDoc(), this); (new AsyncEventDispatcher(aKid, mutation))->RunDOMEventWhenSafe(); } } return NS_OK;
--- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -787,16 +787,44 @@ ContentEventHandler::GetLineBreakType(Wi /* static */ LineBreakType ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak) { return aUseNativeLineBreak ? LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP; } +nsresult +ContentEventHandler::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent) +{ + switch (aEvent->mMessage) { + case NS_QUERY_SELECTED_TEXT: + return OnQuerySelectedText(aEvent); + case NS_QUERY_TEXT_CONTENT: + return OnQueryTextContent(aEvent); + case NS_QUERY_CARET_RECT: + return OnQueryCaretRect(aEvent); + case NS_QUERY_TEXT_RECT: + return OnQueryTextRect(aEvent); + case NS_QUERY_EDITOR_RECT: + return OnQueryEditorRect(aEvent); + case NS_QUERY_CONTENT_STATE: + return OnQueryContentState(aEvent); + case NS_QUERY_SELECTION_AS_TRANSFERABLE: + return OnQuerySelectionAsTransferable(aEvent); + case NS_QUERY_CHARACTER_AT_POINT: + return OnQueryCharacterAtPoint(aEvent); + case NS_QUERY_DOM_WIDGET_HITTEST: + return OnQueryDOMWidgetHittest(aEvent); + default: + return NS_ERROR_NOT_IMPLEMENTED; + } + return NS_OK; +} + // Similar to nsFrameSelection::GetFrameForNodeOffset, // but this is more flexible for OnQueryTextRect to use static nsresult GetFrameForTextRect(nsINode* aNode, int32_t aNodeOffset, bool aHint, nsIFrame** aReturnFrame) { NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
--- a/dom/events/ContentEventHandler.h +++ b/dom/events/ContentEventHandler.h @@ -34,16 +34,19 @@ enum LineBreakType class MOZ_STACK_CLASS ContentEventHandler { public: typedef dom::Selection Selection; explicit ContentEventHandler(nsPresContext* aPresContext); + // Handle aEvent in the current process. + nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent); + // NS_QUERY_SELECTED_TEXT event handler nsresult OnQuerySelectedText(WidgetQueryContentEvent* aEvent); // NS_QUERY_TEXT_CONTENT event handler nsresult OnQueryTextContent(WidgetQueryContentEvent* aEvent); // NS_QUERY_CARET_RECT event handler nsresult OnQueryCaretRect(WidgetQueryContentEvent* aEvent); // NS_QUERY_TEXT_RECT event handler nsresult OnQueryTextRect(WidgetQueryContentEvent* aEvent);
--- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -68,29 +68,29 @@ static const uint32_t kAllMutationBits = NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT | NS_EVENT_BITS_MUTATION_ATTRMODIFIED | NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; static uint32_t MutationBitForEventType(EventMessage aEventType) { switch (aEventType) { - case NS_MUTATION_SUBTREEMODIFIED: + case eLegacySubtreeModified: return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED; - case NS_MUTATION_NODEINSERTED: + case eLegacyNodeInserted: return NS_EVENT_BITS_MUTATION_NODEINSERTED; - case NS_MUTATION_NODEREMOVED: + case eLegacyNodeRemoved: return NS_EVENT_BITS_MUTATION_NODEREMOVED; - case NS_MUTATION_NODEREMOVEDFROMDOCUMENT: + case eLegacyNodeRemovedFromDocument: return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT; - case NS_MUTATION_NODEINSERTEDINTODOCUMENT: + case eLegacyNodeInsertedIntoDocument: return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT; - case NS_MUTATION_ATTRMODIFIED: + case eLegacyAttrModified: return NS_EVENT_BITS_MUTATION_ATTRMODIFIED; - case NS_MUTATION_CHARACTERDATAMODIFIED: + case eLegacyCharacterDataModified: return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; default: break; } return 0; } uint32_t EventListenerManager::sMainThreadCreatedCount = 0; @@ -294,32 +294,32 @@ EventListenerManager::AddEventListenerIn } if (aEventMessage == NS_AFTERPAINT) { mMayHavePaintEventListener = true; nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { window->SetHasPaintEventListeners(); } - } else if (aEventMessage >= NS_MUTATION_START && - aEventMessage <= NS_MUTATION_END) { + } else if (aEventMessage >= eLegacyMutationEventFirst && + aEventMessage <= eLegacyMutationEventLast) { // For mutation listeners, we need to update the global bit on the DOM window. // Otherwise we won't actually fire the mutation event. mMayHaveMutationListeners = true; // Go from our target to the nearest enclosing DOM window. nsPIDOMWindow* window = GetInnerWindowForTarget(); if (window) { nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); if (doc) { doc->WarnOnceAbout(nsIDocument::eMutationEvent); } - // If aEventMessage is NS_MUTATION_SUBTREEMODIFIED, we need to listen all + // If aEventMessage is eLegacySubtreeModified, we need to listen all // mutations. nsContentUtils::HasMutationListeners relies on this. window->SetMutationListeners( - (aEventMessage == NS_MUTATION_SUBTREEMODIFIED) ? + (aEventMessage == eLegacySubtreeModified) ? kAllMutationBits : MutationBitForEventType(aEventMessage)); } } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) { EnableDevice(NS_DEVICE_ORIENTATION); } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) { EnableDevice(NS_DEVICE_PROXIMITY); } else if (aTypeAtom == nsGkAtoms::ondevicelight) { EnableDevice(NS_DEVICE_LIGHT); @@ -1221,37 +1221,37 @@ EventListenerManager::RemoveListenerForA bool EventListenerManager::HasMutationListeners() { if (mMayHaveMutationListeners) { uint32_t count = mListeners.Length(); for (uint32_t i = 0; i < count; ++i) { Listener* listener = &mListeners.ElementAt(i); - if (listener->mEventMessage >= NS_MUTATION_START && - listener->mEventMessage <= NS_MUTATION_END) { + if (listener->mEventMessage >= eLegacyMutationEventFirst && + listener->mEventMessage <= eLegacyMutationEventLast) { return true; } } } return false; } uint32_t EventListenerManager::MutationListenerBits() { uint32_t bits = 0; if (mMayHaveMutationListeners) { uint32_t count = mListeners.Length(); for (uint32_t i = 0; i < count; ++i) { Listener* listener = &mListeners.ElementAt(i); - if (listener->mEventMessage >= NS_MUTATION_START && - listener->mEventMessage <= NS_MUTATION_END) { - if (listener->mEventMessage == NS_MUTATION_SUBTREEMODIFIED) { + if (listener->mEventMessage >= eLegacyMutationEventFirst && + listener->mEventMessage <= eLegacyMutationEventLast) { + if (listener->mEventMessage == eLegacySubtreeModified) { return kAllMutationBits; } bits |= MutationBitForEventType(listener->mEventMessage); } } } return bits; }
--- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -499,21 +499,21 @@ WINDOW_EVENT(offline, eOffline, EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(online, eOnline, EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(pagehide, - NS_PAGE_HIDE, + ePageHide, EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(pageshow, - NS_PAGE_SHOW, + ePageShow, EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) WINDOW_EVENT(popstate, ePopState, EventNameType_XUL | EventNameType_HTMLBodyOrFramesetOnly, eBasicEventClass) // Not supported yet // WINDOW_EVENT(redo) @@ -586,41 +586,41 @@ DOCUMENT_ONLY_EVENT(readystatechange, eBasicEventClass) NON_IDL_EVENT(MozMouseHittest, eMouseHitTest, EventNameType_None, eMouseEventClass) NON_IDL_EVENT(DOMAttrModified, - NS_MUTATION_ATTRMODIFIED, + eLegacyAttrModified, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMCharacterDataModified, - NS_MUTATION_CHARACTERDATAMODIFIED, + eLegacyCharacterDataModified, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMNodeInserted, - NS_MUTATION_NODEINSERTED, + eLegacyNodeInserted, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMNodeRemoved, - NS_MUTATION_NODEREMOVED, + eLegacyNodeRemoved, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMNodeInsertedIntoDocument, - NS_MUTATION_NODEINSERTEDINTODOCUMENT, + eLegacyNodeInsertedIntoDocument, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMNodeRemovedFromDocument, - NS_MUTATION_NODEREMOVEDFROMDOCUMENT, + eLegacyNodeRemovedFromDocument, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMSubtreeModified, - NS_MUTATION_SUBTREEMODIFIED, + eLegacySubtreeModified, EventNameType_HTMLXUL, eMutationEventClass) NON_IDL_EVENT(DOMActivate, eLegacyDOMActivate, EventNameType_HTMLXUL, eUIEventClass) NON_IDL_EVENT(DOMFocusIn, @@ -737,42 +737,42 @@ NON_IDL_EVENT(overflow, eBasicEventClass) NON_IDL_EVENT(underflow, NS_SCROLLPORT_UNDERFLOW, EventNameType_XUL, eBasicEventClass) // Various SVG events NON_IDL_EVENT(SVGLoad, - NS_SVG_LOAD, + eSVGLoad, EventNameType_None, eBasicEventClass) NON_IDL_EVENT(SVGUnload, - NS_SVG_UNLOAD, + eSVGUnload, EventNameType_None, eBasicEventClass) NON_IDL_EVENT(SVGResize, - NS_SVG_RESIZE, + eSVGResize, EventNameType_None, eBasicEventClass) NON_IDL_EVENT(SVGScroll, - NS_SVG_SCROLL, + eSVGScroll, EventNameType_None, eBasicEventClass) NON_IDL_EVENT(SVGZoom, - NS_SVG_ZOOM, + eSVGZoom, EventNameType_None, eSVGZoomEventClass) // Only map the ID to the real event name when MESSAGE_TO_EVENT is defined. #ifndef MESSAGE_TO_EVENT // This is a bit hackish, but SVG's event names are weird. NON_IDL_EVENT(zoom, - NS_SVG_ZOOM, + eSVGZoom, EventNameType_SVGSVG, eBasicEventClass) #endif // Only map the ID to the real event name when MESSAGE_TO_EVENT is defined. #ifndef MESSAGE_TO_EVENT NON_IDL_EVENT(begin, NS_SMIL_BEGIN, EventNameType_SMIL,
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -537,16 +537,21 @@ EventStateManager::PreHandleEvent(nsPres sLastScreenPoint = UIEvent::CalculateScreenPoint(aPresContext, aEvent); sLastClientPoint = UIEvent::CalculateClientPoint(aPresContext, aEvent, nullptr); } *aStatus = nsEventStatus_eIgnore; + if (aEvent->mClass == eQueryContentEventClass) { + HandleQueryContentEvent(aEvent->AsQueryContentEvent()); + return NS_OK; + } + switch (aEvent->mMessage) { case eContextMenu: if (sIsPointerLocked) { return NS_ERROR_DOM_INVALID_STATE_ERR; } break; case eMouseDown: { switch (mouseEvent->button) { @@ -733,83 +738,16 @@ EventStateManager::PreHandleEvent(nsPres // Init lineOrPageDelta values for line scroll events for some devices // on some platforms which might dispatch wheel events which don't have // lineOrPageDelta values. And also, if delta values are customized by // prefs, this recomputes them. DeltaAccumulator::GetInstance()-> InitLineOrPageDelta(aTargetFrame, this, wheelEvent); } break; - case NS_QUERY_SELECTED_TEXT: - DoQuerySelectedText(aEvent->AsQueryContentEvent()); - break; - case NS_QUERY_TEXT_CONTENT: - { - if (RemoteQueryContentEvent(aEvent)) { - break; - } - ContentEventHandler handler(mPresContext); - handler.OnQueryTextContent(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_CARET_RECT: - { - if (RemoteQueryContentEvent(aEvent)) { - break; - } - ContentEventHandler handler(mPresContext); - handler.OnQueryCaretRect(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_TEXT_RECT: - { - if (RemoteQueryContentEvent(aEvent)) { - break; - } - ContentEventHandler handler(mPresContext); - handler.OnQueryTextRect(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_EDITOR_RECT: - { - if (RemoteQueryContentEvent(aEvent)) { - break; - } - ContentEventHandler handler(mPresContext); - handler.OnQueryEditorRect(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_CONTENT_STATE: - { - // XXX remote event - ContentEventHandler handler(mPresContext); - handler.OnQueryContentState(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_SELECTION_AS_TRANSFERABLE: - { - // XXX remote event - ContentEventHandler handler(mPresContext); - handler.OnQuerySelectionAsTransferable(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_CHARACTER_AT_POINT: - { - // XXX remote event - ContentEventHandler handler(mPresContext); - handler.OnQueryCharacterAtPoint(aEvent->AsQueryContentEvent()); - } - break; - case NS_QUERY_DOM_WIDGET_HITTEST: - { - // XXX remote event - ContentEventHandler handler(mPresContext); - handler.OnQueryDOMWidgetHittest(aEvent->AsQueryContentEvent()); - } - break; case NS_SELECTION_SET: IMEStateManager::HandleSelectionEvent(aPresContext, GetFocusedContent(), aEvent->AsSelectionEvent()); break; case NS_CONTENT_COMMAND_CUT: case NS_CONTENT_COMMAND_COPY: case NS_CONTENT_COMMAND_PASTE: case NS_CONTENT_COMMAND_DELETE: @@ -827,27 +765,63 @@ EventStateManager::PreHandleEvent(nsPres break; case NS_COMPOSITION_START: if (aEvent->mFlags.mIsTrusted) { // If the event is trusted event, set the selected text to data of // composition event. WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent(); WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT, compositionEvent->widget); - DoQuerySelectedText(&selectedText); + HandleQueryContentEvent(&selectedText); NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); compositionEvent->mData = selectedText.mReply.mString; } break; default: break; } return NS_OK; } +void +EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent) +{ + switch (aEvent->mMessage) { + case NS_QUERY_SELECTED_TEXT: + case NS_QUERY_TEXT_CONTENT: + case NS_QUERY_CARET_RECT: + case NS_QUERY_TEXT_RECT: + case NS_QUERY_EDITOR_RECT: + if (!IsTargetCrossProcess(aEvent)) { + break; + } + // Will not be handled locally, remote the event + GetCrossProcessTarget()->HandleQueryContentEvent(*aEvent); + return; + // Following events have not been supported in e10s mode yet. + case NS_QUERY_CONTENT_STATE: + case NS_QUERY_SELECTION_AS_TRANSFERABLE: + case NS_QUERY_CHARACTER_AT_POINT: + case NS_QUERY_DOM_WIDGET_HITTEST: + break; + default: + return; + } + + // If there is an IMEContentObserver, we need to handle QueryContentEvent + // with it. + if (mIMEContentObserver) { + mIMEContentObserver->HandleQueryContentEvent(aEvent); + return; + } + + ContentEventHandler handler(mPresContext); + handler.HandleQueryContentEvent(aEvent); +} + // static int32_t EventStateManager::GetAccessModifierMaskFor(nsISupports* aDocShell) { nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell)); if (!treeItem) return -1; // invalid modifier @@ -3363,28 +3337,16 @@ EventStateManager::PostHandleEvent(nsPre //Reset target frame to null to avoid mistargeting after reentrant event mCurrentTarget = nullptr; mCurrentTargetContent = nullptr; return ret; } -bool -EventStateManager::RemoteQueryContentEvent(WidgetEvent* aEvent) -{ - WidgetQueryContentEvent* queryEvent = aEvent->AsQueryContentEvent(); - if (!IsTargetCrossProcess(queryEvent)) { - return false; - } - // Will not be handled locally, remote the event - GetCrossProcessTarget()->HandleQueryContentEvent(*queryEvent); - return true; -} - TabParent* EventStateManager::GetCrossProcessTarget() { return IMEStateManager::GetActiveTabParent(); } bool EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent) @@ -5185,26 +5147,16 @@ EventStateManager::DoContentCommandScrol } // The caller may want synchronous scrolling. sf->ScrollBy(pt, scrollUnit, nsIScrollableFrame::INSTANT); return NS_OK; } void -EventStateManager::DoQuerySelectedText(WidgetQueryContentEvent* aEvent) -{ - if (RemoteQueryContentEvent(aEvent)) { - return; - } - ContentEventHandler handler(mPresContext); - handler.OnQuerySelectedText(aEvent); -} - -void EventStateManager::SetActiveManager(EventStateManager* aNewESM, nsIContent* aContent) { if (sActiveESM && aNewESM != sActiveESM) { sActiveESM->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE); } sActiveESM = aNewESM; if (sActiveESM && aContent) {
--- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -804,30 +804,29 @@ protected: * BeginTrackingDragGesture). aEvent->widget must be * mCurrentTarget->GetNearestWidget(). */ void FillInEventFromGestureDown(WidgetMouseEvent* aEvent); nsresult DoContentCommandEvent(WidgetContentCommandEvent* aEvent); nsresult DoContentCommandScrollEvent(WidgetContentCommandEvent* aEvent); - void DoQuerySelectedText(WidgetQueryContentEvent* aEvent); - - bool RemoteQueryContentEvent(WidgetEvent* aEvent); dom::TabParent *GetCrossProcessTarget(); bool IsTargetCrossProcess(WidgetGUIEvent* aEvent); bool DispatchCrossProcessEvent(WidgetEvent* aEvent, nsFrameLoader* aRemote, nsEventStatus *aStatus); bool HandleCrossProcessEvent(WidgetEvent* aEvent, nsEventStatus* aStatus); void ReleaseCurrentIMEContentObserver(); + void HandleQueryContentEvent(WidgetQueryContentEvent* aEvent); + private: static inline void DoStateChange(dom::Element* aElement, EventStates aState, bool aAddState); static inline void DoStateChange(nsIContent* aContent, EventStates aState, bool aAddState); static void UpdateAncestorState(nsIContent* aStartNode, nsIContent* aStopBefore, EventStates aState,
--- a/dom/events/IMEContentObserver.cpp +++ b/dom/events/IMEContentObserver.cpp @@ -1,14 +1,16 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/Logging.h" + #include "ContentEventHandler.h" #include "IMEContentObserver.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/AutoRestore.h" #include "mozilla/EventStateManager.h" #include "mozilla/IMEStateManager.h" #include "mozilla/MouseEvents.h" #include "mozilla/TextComposition.h" @@ -32,16 +34,115 @@ #include "nsPresContext.h" #include "nsWeakReference.h" #include "WritingModes.h" namespace mozilla { using namespace widget; +PRLogModuleInfo* sIMECOLog = nullptr; + +static const char* +ToChar(bool aBool) +{ + return aBool ? "true" : "false"; +} + +static const char* +ToChar(EventMessage aEventMessage) +{ + switch (aEventMessage) { + case NS_QUERY_SELECTED_TEXT: + return "NS_QUERY_SELECTED_TEXT"; + case NS_QUERY_TEXT_CONTENT: + return "NS_QUERY_TEXT_CONTENT"; + case NS_QUERY_CARET_RECT: + return "NS_QUERY_CARET_RECT"; + case NS_QUERY_TEXT_RECT: + return "NS_QUERY_TEXT_RECT"; + case NS_QUERY_EDITOR_RECT: + return "NS_QUERY_EDITOR_RECT"; + case NS_QUERY_CONTENT_STATE: + return "NS_QUERY_CONTENT_STATE"; + case NS_QUERY_SELECTION_AS_TRANSFERABLE: + return "NS_QUERY_SELECTION_AS_TRANSFERABLE"; + case NS_QUERY_CHARACTER_AT_POINT: + return "NS_QUERY_CHARACTER_AT_POINT"; + case NS_QUERY_DOM_WIDGET_HITTEST: + return "NS_QUERY_DOM_WIDGET_HITTEST"; + default: + return "Unsupprted message"; + } +} + +class WritingModeToString final : public nsAutoCString +{ +public: + explicit WritingModeToString(const WritingMode& aWritingMode) + { + if (!aWritingMode.IsVertical()) { + AssignLiteral("Horizontal"); + return; + } + if (aWritingMode.IsVerticalLR()) { + AssignLiteral("Vertical (LR)"); + return; + } + AssignLiteral("Vertical (RL)"); + } + virtual ~WritingModeToString() {} +}; + +class SelectionChangeDataToString final : public nsAutoCString +{ +public: + explicit SelectionChangeDataToString( + const IMENotification::SelectionChangeDataBase& aData) + { + if (!aData.IsValid()) { + AppendLiteral("{ IsValid()=false }"); + return; + } + AppendPrintf("{ mOffset=%u, ", aData.mOffset); + if (aData.mString->Length() > 20) { + AppendPrintf("mString.Length()=%u, ", aData.mString->Length()); + } else { + AppendPrintf("mString=\"%s\" (Length()=%u), ", + NS_ConvertUTF16toUTF8(*aData.mString).get(), + aData.mString->Length()); + } + AppendPrintf("GetWritingMode()=%s, mReversed=%s, mCausedByComposition=%s, " + "mCausedBySelectionEvent=%s }", + WritingModeToString(aData.GetWritingMode()).get(), + ToChar(aData.mReversed), + ToChar(aData.mCausedByComposition), + ToChar(aData.mCausedBySelectionEvent)); + } + virtual ~SelectionChangeDataToString() {} +}; + +class TextChangeDataToString final : public nsAutoCString +{ +public: + explicit TextChangeDataToString( + const IMENotification::TextChangeDataBase& aData) + { + if (!aData.IsValid()) { + AppendLiteral("{ IsValid()=false }"); + return; + } + AppendPrintf("{ mStartOffset=%u, mRemovedEndOffset=%u, mAddedEndOffset=%u, " + "mCausedByComposition=%s }", aData.mStartOffset, + aData.mRemovedEndOffset, aData.mAddedEndOffset, + ToChar(aData.mCausedByComposition)); + } + virtual ~TextChangeDataToString() {} +}; + /****************************************************************************** * mozilla::IMEContentObserver ******************************************************************************/ NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver) nsAutoScriptBlocker scriptBlocker; @@ -58,16 +159,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IM NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartOfRemovingTextRangeCache.mContainerNode) tmp->mUpdatePreference.mWantUpdates = nsIMEUpdatePreference::NOTIFY_NOTHING; tmp->mESM = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IMEContentObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWidget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedWidget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootContent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditableNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditor) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndOfAddedTextCache.mContainerNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE( mStartOfRemovingTextRangeCache.mContainerNode) @@ -97,16 +199,19 @@ IMEContentObserver::IMEContentObserver() , mSelectionChangeCausedOnlyByComposition(false) , mSelectionChangeCausedOnlyBySelectionEvent(false) , mIsPositionChangeEventPending(false) , mIsFlushingPendingNotifications(false) { #ifdef DEBUG mTextChangeData.Test(); #endif + if (!sIMECOLog) { + sIMECOLog = PR_NewLogModule("IMEContentObserver"); + } } void IMEContentObserver::Init(nsIWidget* aWidget, nsPresContext* aPresContext, nsIContent* aContent, nsIEditor* aEditor) { @@ -251,26 +356,36 @@ IMEContentObserver::NotifyIMEOfBlur() // If we hasn't been set focus, we shouldn't send blur notification to IME. if (!mIMEHasFocus) { return; } // mWidget must have been non-nullptr if IME has focus. MOZ_RELEASE_ASSERT(widget); + nsRefPtr<IMEContentObserver> kungFuDeathGrip(this); + + MOZ_LOG(sIMECOLog, LogLevel::Info, + ("IMECO: 0x%p IMEContentObserver::NotifyIMEOfBlur(), " + "sending NOTIFY_IME_OF_BLUR", this)); + // For now, we need to send blur notification in any condition because // we don't have any simple ways to send blur notification asynchronously. // After this call, Destroy() or Unlink() will stop observing the content // and forget everything. Therefore, if it's not safe to send notification // when script blocker is unlocked, we cannot send blur notification after // that and before next focus notification. // Anyway, as far as we know, IME doesn't try to query content when it loses // focus. So, this may not cause any problem. mIMEHasFocus = false; IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_BLUR), widget); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::NotifyIMEOfBlur(), " + "sent NOTIFY_IME_OF_BLUR", this)); } void IMEContentObserver::UnregisterObservers() { if (!mIsObserving) { return; } @@ -280,16 +395,18 @@ IMEContentObserver::UnregisterObservers( mEditor->RemoveEditorObserver(this); } if (mUpdatePreference.WantSelectionChange() && mSelection) { nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection)); if (selPrivate) { selPrivate->RemoveSelectionListener(this); } + mSelectionData.Clear(); + mFocusedWidget = nullptr; } if (mUpdatePreference.WantTextChange() && mRootContent) { mRootContent->RemoveMutationObserver(this); } if (mUpdatePreference.WantPositionChanged() && mDocShell) { mDocShell->RemoveWeakScrollObserver(this); @@ -438,16 +555,51 @@ IMEContentObserver::Reflow(DOMHighResTim NS_IMETHODIMP IMEContentObserver::ReflowInterruptible(DOMHighResTimeStamp aStart, DOMHighResTimeStamp aEnd) { MaybeNotifyIMEOfPositionChange(); return NS_OK; } +nsresult +IMEContentObserver::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent) +{ + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ " + "mMessage=%s })", this, ToChar(aEvent->mMessage))); + + // If the instance has cache, it should use the cached selection which was + // sent to the widget. + if (aEvent->mMessage == NS_QUERY_SELECTED_TEXT && + aEvent->mUseNativeLineBreak && + mSelectionData.IsValid()) { + aEvent->mReply.mContentsRoot = mRootContent; + aEvent->mReply.mHasSelection = !mSelectionData.IsCollapsed(); + aEvent->mReply.mOffset = mSelectionData.mOffset; + aEvent->mReply.mString = mSelectionData.String(); + aEvent->mReply.mWritingMode = mSelectionData.GetWritingMode(); + aEvent->mReply.mReversed = mSelectionData.mReversed; + aEvent->mSucceeded = true; + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::HandleQueryContentEvent(aEvent={ " + "mMessage=%s })", this, ToChar(aEvent->mMessage))); + return NS_OK; + } + + ContentEventHandler handler(GetPresContext()); + nsresult rv = handler.HandleQueryContentEvent(aEvent); + if (aEvent->mSucceeded) { + // We need to guarantee that mRootContent should be always same value for + // the observing editor. + aEvent->mReply.mContentsRoot = mRootContent; + } + return rv; +} + bool IMEContentObserver::OnMouseButtonEvent(nsPresContext* aPresContext, WidgetMouseEvent* aMouseEvent) { if (!mUpdatePreference.WantMouseButtonEventOnChar()) { return false; } if (!aMouseEvent->mFlags.mIsTrusted || @@ -781,77 +933,174 @@ IMEContentObserver::AttributeChanged(nsI LINE_BREAK_TYPE_NATIVE); NS_ENSURE_SUCCESS_VOID(rv); TextChangeData data(start, start + mPreAttrChangeLength, start + postAttrChangeLength, causedByComposition); MaybeNotifyIMEOfTextChange(data); } +void +IMEContentObserver::SuppressNotifyingIME() +{ + mSuppressNotifications++; + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::SuppressNotifyingIME(), " + "mSuppressNotifications=%u", this, mSuppressNotifications)); +} + +void +IMEContentObserver::UnsuppressNotifyingIME() +{ + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::UnsuppressNotifyingIME(), " + "mSuppressNotifications=%u", this, mSuppressNotifications)); + + if (!mSuppressNotifications || --mSuppressNotifications) { + return; + } + FlushMergeableNotifications(); +} + NS_IMETHODIMP IMEContentObserver::EditAction() { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::EditAction()", this)); + mEndOfAddedTextCache.Clear(); mStartOfRemovingTextRangeCache.Clear(); FlushMergeableNotifications(); return NS_OK; } NS_IMETHODIMP IMEContentObserver::BeforeEditAction() { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::BeforeEditAction()", this)); + mEndOfAddedTextCache.Clear(); mStartOfRemovingTextRangeCache.Clear(); return NS_OK; } NS_IMETHODIMP IMEContentObserver::CancelEditAction() { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::CancelEditAction()", this)); + mEndOfAddedTextCache.Clear(); mStartOfRemovingTextRangeCache.Clear(); FlushMergeableNotifications(); return NS_OK; } void IMEContentObserver::PostFocusSetNotification() { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PostFocusSetNotification()", this)); + mIsFocusEventPending = true; } void IMEContentObserver::PostTextChangeNotification( const TextChangeDataBase& aTextChangeData) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PostTextChangeNotification(" + "aTextChangeData=%s)", + this, TextChangeDataToString(aTextChangeData).get())); + mTextChangeData += aTextChangeData; MOZ_ASSERT(mTextChangeData.IsValid(), "mTextChangeData must have text change data"); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PostTextChangeNotification(), " + "mTextChangeData=%s)", + this, TextChangeDataToString(mTextChangeData).get())); } void IMEContentObserver::PostSelectionChangeNotification( bool aCausedByComposition, bool aCausedBySelectionEvent) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PostSelectionChangeNotification(" + "aCausedByComposition=%s, aCausedBySelectionEvent=%s)", + this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent))); + if (!mIsSelectionChangeEventPending) { mSelectionChangeCausedOnlyByComposition = aCausedByComposition; } else { mSelectionChangeCausedOnlyByComposition = mSelectionChangeCausedOnlyByComposition && aCausedByComposition; } if (!mSelectionChangeCausedOnlyBySelectionEvent) { mSelectionChangeCausedOnlyBySelectionEvent = aCausedBySelectionEvent; } else { mSelectionChangeCausedOnlyBySelectionEvent = mSelectionChangeCausedOnlyBySelectionEvent && aCausedBySelectionEvent; } mIsSelectionChangeEventPending = true; } +void +IMEContentObserver::MaybeNotifyIMEOfFocusSet() +{ + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfFocusSet()", this)); + + PostFocusSetNotification(); + FlushMergeableNotifications(); +} + +void +IMEContentObserver::MaybeNotifyIMEOfTextChange( + const TextChangeDataBase& aTextChangeData) +{ + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfTextChange(" + "aTextChangeData=%s)", + this, TextChangeDataToString(aTextChangeData).get())); + + PostTextChangeNotification(aTextChangeData); + FlushMergeableNotifications(); +} + +void +IMEContentObserver::MaybeNotifyIMEOfSelectionChange( + bool aCausedByComposition, + bool aCausedBySelectionEvent) +{ + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfSelectionChange(" + "aCausedByComposition=%s, aCausedBySelectionEvent=%s)", + this, ToChar(aCausedByComposition), ToChar(aCausedBySelectionEvent))); + + PostSelectionChangeNotification(aCausedByComposition, + aCausedBySelectionEvent); + FlushMergeableNotifications(); +} + +void +IMEContentObserver::MaybeNotifyIMEOfPositionChange() +{ + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::MaybeNotifyIMEOfPositionChange()", this)); + PostPositionChangeNotification(); + FlushMergeableNotifications(); +} + bool IMEContentObserver::UpdateSelectionCache() { MOZ_ASSERT(IsSafeToNotifyIME()); if (!mUpdatePreference.WantSelectionChange()) { return false; } @@ -862,28 +1111,38 @@ IMEContentObserver::UpdateSelectionCache // selection offset and writing mode? WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, mWidget); ContentEventHandler handler(GetPresContext()); handler.OnQuerySelectedText(&selection); if (NS_WARN_IF(!selection.mSucceeded)) { return false; } + mFocusedWidget = selection.mReply.mFocusedWidget; mSelectionData.mOffset = selection.mReply.mOffset; *mSelectionData.mString = selection.mReply.mString; mSelectionData.SetWritingMode(selection.GetWritingMode()); mSelectionData.mReversed = selection.mReply.mReversed; mSelectionData.mCausedByComposition = false; mSelectionData.mCausedBySelectionEvent = false; + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::UpdateSelectionCache(), " + "mSelectionData=%s", + this, SelectionChangeDataToString(mSelectionData).get())); + return mSelectionData.IsValid(); } void IMEContentObserver::PostPositionChangeNotification() { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PostPositionChangeNotification()", this)); + mIsPositionChangeEventPending = true; } bool IMEContentObserver::IsReflowLocked() const { nsPresContext* presContext = GetPresContext(); if (NS_WARN_IF(!presContext)) { @@ -933,71 +1192,96 @@ IMEContentObserver::IsSafeToNotifyIME() return true; } void IMEContentObserver::FlushMergeableNotifications() { if (!IsSafeToNotifyIME()) { // So, if this is already called, this should do nothing. + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "FAILED, due to unsafe to notify IME", this)); return; } // Notifying something may cause nested call of this method. For example, // when somebody notified one of the notifications may dispatch query content // event. Then, it causes flushing layout which may cause another layout // change notification. if (mIsFlushingPendingNotifications) { // So, if this is already called, this should do nothing. + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "FAILED, due to already flushing pending notifications", this)); return; } AutoRestore<bool> flusing(mIsFlushingPendingNotifications); mIsFlushingPendingNotifications = true; // NOTE: Reset each pending flag because sending notification may cause // another change. if (mIsFocusEventPending) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "creating FocusSetEvent...", this)); mIsFocusEventPending = false; nsContentUtils::AddScriptRunner(new FocusSetEvent(this)); // This is the first notification to IME. So, we don't need to notify any // more since IME starts to query content after it gets focus. ClearPendingNotifications(); return; } if (mTextChangeData.IsValid()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "creating TextChangeEvent...", this)); nsContentUtils::AddScriptRunner(new TextChangeEvent(this, mTextChangeData)); } // Be aware, PuppetWidget depends on the order of this. A selection change // notification should not be sent before a text change notification because // PuppetWidget shouldn't query new text content every selection change. if (mIsSelectionChangeEventPending) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "creating SelectionChangeEvent...", this)); mIsSelectionChangeEventPending = false; nsContentUtils::AddScriptRunner( new SelectionChangeEvent(this, mSelectionChangeCausedOnlyByComposition, mSelectionChangeCausedOnlyBySelectionEvent)); } if (mIsPositionChangeEventPending) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "creating PositionChangeEvent...", this)); mIsPositionChangeEventPending = false; nsContentUtils::AddScriptRunner(new PositionChangeEvent(this)); } // If notifications may cause new change, we should notify them now. if (mTextChangeData.IsValid() || mIsSelectionChangeEventPending || mIsPositionChangeEventPending) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "posting AsyncMergeableNotificationsFlusher to current thread", this)); nsRefPtr<AsyncMergeableNotificationsFlusher> asyncFlusher = new AsyncMergeableNotificationsFlusher(this); NS_DispatchToCurrentThread(asyncFlusher); } + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FlushMergeableNotifications(), " + "finished", this)); } /****************************************************************************** * mozilla::IMEContentObserver::AChangeEvent ******************************************************************************/ bool IMEContentObserver::AChangeEvent::CanNotifyIME() const @@ -1047,137 +1331,218 @@ IMEContentObserver::AChangeEvent::IsSafe ******************************************************************************/ NS_IMETHODIMP IMEContentObserver::FocusSetEvent::Run() { if (!CanNotifyIME()) { // If IMEContentObserver has already gone, we don't need to notify IME of // focus. + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FocusSetEvent::Run(), FAILED, due to " + "impossible to notify IME of focus", this)); mIMEContentObserver->ClearPendingNotifications(); return NS_OK; } if (!IsSafeToNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FocusSetEvent::Run(), retrying to " + "send NOTIFY_IME_OF_FOCUS...", this)); mIMEContentObserver->PostFocusSetNotification(); return NS_OK; } mIMEContentObserver->mIMEHasFocus = true; // Initialize selection cache with the first selection data. mIMEContentObserver->UpdateSelectionCache(); + + MOZ_LOG(sIMECOLog, LogLevel::Info, + ("IMECO: 0x%p IMEContentObserver::FocusSetEvent::Run(), " + "sending NOTIFY_IME_OF_FOCUS...", this)); + IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_FOCUS), mIMEContentObserver->mWidget); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::FocusSetEvent::Run(), " + "sent NOTIFY_IME_OF_FOCUS", this)); return NS_OK; } /****************************************************************************** * mozilla::IMEContentObserver::SelectionChangeEvent ******************************************************************************/ NS_IMETHODIMP IMEContentObserver::SelectionChangeEvent::Run() { if (!CanNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), FAILED, " + "due to impossible to notify IME of selection change", this)); return NS_OK; } if (!IsSafeToNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), " + "retrying to send NOTIFY_IME_OF_SELECTION_CHANGE...", this)); mIMEContentObserver->PostSelectionChangeNotification( mCausedByComposition, mCausedBySelectionEvent); return NS_OK; } SelectionChangeData lastSelChangeData = mIMEContentObserver->mSelectionData; if (NS_WARN_IF(!mIMEContentObserver->UpdateSelectionCache())) { + MOZ_LOG(sIMECOLog, LogLevel::Error, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), FAILED, " + "due to UpdateSelectionCache() failure", this)); return NS_OK; } // If the IME doesn't want selection change notifications caused by // composition, we should do nothing anymore. if (mCausedByComposition && !mIMEContentObserver-> mUpdatePreference.WantChangesCausedByComposition()) { return NS_OK; } // The state may be changed since querying content causes flushing layout. if (!CanNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), FAILED, " + "due to flushing layout having changed something", this)); return NS_OK; } // If the selection isn't changed actually, we shouldn't notify IME of // selection change. SelectionChangeData& newSelChangeData = mIMEContentObserver->mSelectionData; if (lastSelChangeData.IsValid() && lastSelChangeData.mOffset == newSelChangeData.mOffset && lastSelChangeData.String() == newSelChangeData.String() && lastSelChangeData.GetWritingMode() == newSelChangeData.GetWritingMode() && lastSelChangeData.mReversed == newSelChangeData.mReversed) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), not " + "notifying IME of NOTIFY_IME_OF_SELECTION_CHANGE due to not changed " + "actually", this)); return NS_OK; } + MOZ_LOG(sIMECOLog, LogLevel::Info, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), " + "sending NOTIFY_IME_OF_SELECTION_CHANGE... newSelChangeData=%s", + this, SelectionChangeDataToString(newSelChangeData).get())); + IMENotification notification(NOTIFY_IME_OF_SELECTION_CHANGE); notification.SetData(mIMEContentObserver->mSelectionData, mCausedByComposition, mCausedBySelectionEvent); IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::SelectionChangeEvent::Run(), " + "sent NOTIFY_IME_OF_SELECTION_CHANGE", this)); return NS_OK; } /****************************************************************************** * mozilla::IMEContentObserver::TextChangeEvent ******************************************************************************/ NS_IMETHODIMP IMEContentObserver::TextChangeEvent::Run() { if (!CanNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::TextChangeEvent::Run(), FAILED, " + "due to impossible to notify IME of text change", this)); return NS_OK; } if (!IsSafeToNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::TextChangeEvent::Run(), retrying to " + "send NOTIFY_IME_OF_TEXT_CHANGE...", this)); mIMEContentObserver->PostTextChangeNotification(mTextChangeData); return NS_OK; } + MOZ_LOG(sIMECOLog, LogLevel::Info, + ("IMECO: 0x%p IMEContentObserver::TextChangeEvent::Run(), " + "sending NOTIFY_IME_OF_TEXT_CHANGE... mTextChangeData=%s", + this, TextChangeDataToString(mTextChangeData).get())); + IMENotification notification(NOTIFY_IME_OF_TEXT_CHANGE); notification.SetData(mTextChangeData); IMEStateManager::NotifyIME(notification, mIMEContentObserver->mWidget); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::TextChangeEvent::Run(), " + "sent NOTIFY_IME_OF_TEXT_CHANGE", this)); return NS_OK; } /****************************************************************************** * mozilla::IMEContentObserver::PositionChangeEvent ******************************************************************************/ NS_IMETHODIMP IMEContentObserver::PositionChangeEvent::Run() { if (!CanNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PositionChangeEvent::Run(), FAILED, " + "due to impossible to notify IME of position change", this)); return NS_OK; } if (!IsSafeToNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PositionChangeEvent::Run(), " + "retrying to send NOTIFY_IME_OF_POSITION_CHANGE...", this)); mIMEContentObserver->PostPositionChangeNotification(); return NS_OK; } + MOZ_LOG(sIMECOLog, LogLevel::Info, + ("IMECO: 0x%p IMEContentObserver::PositionChangeEvent::Run(), " + "sending NOTIFY_IME_OF_POSITION_CHANGE...", this)); + IMEStateManager::NotifyIME(IMENotification(NOTIFY_IME_OF_POSITION_CHANGE), mIMEContentObserver->mWidget); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::PositionChangeEvent::Run(), " + "sent NOTIFY_IME_OF_POSITION_CHANGE", this)); return NS_OK; } /****************************************************************************** * mozilla::IMEContentObserver::AsyncMergeableNotificationsFlusher ******************************************************************************/ NS_IMETHODIMP IMEContentObserver::AsyncMergeableNotificationsFlusher::Run() { if (!CanNotifyIME()) { + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::AsyncMergeableNotificationsFlusher::" + "Run(), FAILED, due to impossible to flush pending notifications", + this)); return NS_OK; } + MOZ_LOG(sIMECOLog, LogLevel::Info, + ("IMECO: 0x%p IMEContentObserver::AsyncMergeableNotificationsFlusher::" + "Run(), calling FlushMergeableNotifications()...", this)); + mIMEContentObserver->FlushMergeableNotifications(); + + MOZ_LOG(sIMECOLog, LogLevel::Debug, + ("IMECO: 0x%p IMEContentObserver::AsyncMergeableNotificationsFlusher::" + "Run(), called FlushMergeableNotifications()", this)); return NS_OK; } } // namespace mozilla
--- a/dom/events/IMEContentObserver.h +++ b/dom/events/IMEContentObserver.h @@ -61,16 +61,18 @@ public: NS_DECL_NSIREFLOWOBSERVER // nsIScrollObserver virtual void ScrollPositionChanged() override; bool OnMouseButtonEvent(nsPresContext* aPresContext, WidgetMouseEvent* aMouseEvent); + nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent); + void Init(nsIWidget* aWidget, nsPresContext* aPresContext, nsIContent* aContent, nsIEditor* aEditor); void Destroy(); /** * IMEContentObserver is stored by EventStateManager during observing. * DisconnectFromEventStateManager() is called when EventStateManager stops * storing the instance. */ @@ -89,24 +91,18 @@ public: bool IsManaging(nsPresContext* aPresContext, nsIContent* aContent); bool IsEditorHandlingEventForComposition() const; bool KeepAliveDuringDeactive() const { return mUpdatePreference.WantDuringDeactive(); } nsIWidget* GetWidget() const { return mWidget; } nsIEditor* GetEditor() const { return mEditor; } - void SuppressNotifyingIME() { mSuppressNotifications++; } - void UnsuppressNotifyingIME() - { - if (!mSuppressNotifications || --mSuppressNotifications) { - return; - } - FlushMergeableNotifications(); - } + void SuppressNotifyingIME(); + void UnsuppressNotifyingIME(); nsPresContext* GetPresContext() const; nsresult GetSelectionAndRoot(nsISelection** aSelection, nsIContent** aRoot) const; private: ~IMEContentObserver() {} enum State { @@ -117,42 +113,25 @@ private: }; State GetState() const; bool IsObservingContent(nsPresContext* aPresContext, nsIContent* aContent) const; bool IsReflowLocked() const; bool IsSafeToNotifyIME() const; void PostFocusSetNotification(); - void MaybeNotifyIMEOfFocusSet() - { - PostFocusSetNotification(); - FlushMergeableNotifications(); - } + void MaybeNotifyIMEOfFocusSet(); void PostTextChangeNotification(const TextChangeDataBase& aTextChangeData); - void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData) - { - PostTextChangeNotification(aTextChangeData); - FlushMergeableNotifications(); - } + void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData); void PostSelectionChangeNotification(bool aCausedByComposition, bool aCausedBySelectionEvent); void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition, - bool aCausedBySelectionEvent) - { - PostSelectionChangeNotification(aCausedByComposition, - aCausedBySelectionEvent); - FlushMergeableNotifications(); - } + bool aCausedBySelectionEvent); void PostPositionChangeNotification(); - void MaybeNotifyIMEOfPositionChange() - { - PostPositionChangeNotification(); - FlushMergeableNotifications(); - } + void MaybeNotifyIMEOfPositionChange(); void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd); void ObserveEditableNode(); /** * NotifyIMEOfBlur() notifies IME of blur. */ void NotifyIMEOfBlur(); /** @@ -173,16 +152,20 @@ private: * This should be called only when IsSafeToNotifyIME() returns true. * * Note that this does nothing if mUpdatePreference.WantSelectionChange() * returns false. */ bool UpdateSelectionCache(); nsCOMPtr<nsIWidget> mWidget; + // mFocusedWidget has the editor observed by the instance. E.g., if the + // focused editor is in XUL panel, this should be the widget of the panel. + // On the other hand, mWidget is its parent which handles IME. + nsCOMPtr<nsIWidget> mFocusedWidget; nsCOMPtr<nsISelection> mSelection; nsCOMPtr<nsIContent> mRootContent; nsCOMPtr<nsINode> mEditableNode; nsCOMPtr<nsIDocShell> mDocShell; nsCOMPtr<nsIEditor> mEditor; /** * FlatTextCache stores flat text length from start of the content to
--- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -109,16 +109,17 @@ TextComposition::CloneAndDispatchAs( MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->widget), "Should be called only when it's safe to dispatch an event"); WidgetCompositionEvent compositionEvent(aCompositionEvent->mFlags.mIsTrusted, aMessage, aCompositionEvent->widget); compositionEvent.time = aCompositionEvent->time; compositionEvent.timeStamp = aCompositionEvent->timeStamp; compositionEvent.mData = aCompositionEvent->mData; + compositionEvent.mOriginalMessage = aCompositionEvent->mMessage; compositionEvent.mFlags.mIsSynthesizedForTests = aCompositionEvent->mFlags.mIsSynthesizedForTests; nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault; nsEventStatus* status = aStatus ? aStatus : &dummyStatus; if (aMessage == NS_COMPOSITION_UPDATE) { mLastData = compositionEvent.mData; }
--- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -784,24 +784,24 @@ DispatchSuccessEvent(ResultHelper* aResu transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); } } class WorkerPermissionChallenge; // This class calles WorkerPermissionChallenge::OperationCompleted() in the // worker thread. -class WorkerPermissionOperationCompleted final : public WorkerRunnable +class WorkerPermissionOperationCompleted final : public WorkerControlRunnable { nsRefPtr<WorkerPermissionChallenge> mChallenge; public: WorkerPermissionOperationCompleted(WorkerPrivate* aWorkerPrivate, WorkerPermissionChallenge* aChallenge) - : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) + : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) , mChallenge(aChallenge) { MOZ_ASSERT(NS_IsMainThread()); } virtual bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; }; @@ -897,21 +897,17 @@ public: void OperationCompleted() { if (NS_IsMainThread()) { nsRefPtr<WorkerPermissionOperationCompleted> runnable = new WorkerPermissionOperationCompleted(mWorkerPrivate, this); - if (!runnable->Dispatch(nullptr)) { - NS_WARNING("Failed to dispatch a runnable to the worker thread."); - return; - } - + MOZ_ALWAYS_TRUE(runnable->Dispatch(nullptr)); return; } MOZ_ASSERT(mActor); mActor->AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); @@ -1379,17 +1375,17 @@ BackgroundFactoryRequestChild::RecvPermi nsRefPtr<WorkerPermissionChallenge> challenge = new WorkerPermissionChallenge(workerPrivate, this, mFactory, aPrincipalInfo); JSContext* cx = workerPrivate->GetJSContext(); MOZ_ASSERT(cx); - if (!workerPrivate->AddFeature(cx, challenge)) { + if (NS_WARN_IF(!workerPrivate->AddFeature(cx, challenge))) { return false; } MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(challenge))); return true; } nsresult rv; @@ -1401,17 +1397,19 @@ BackgroundFactoryRequestChild::RecvPermi if (XRE_IsParentProcess()) { nsCOMPtr<nsPIDOMWindow> window = mFactory->GetParentObject(); MOZ_ASSERT(window); nsCOMPtr<Element> ownerElement = do_QueryInterface(window->GetChromeEventHandler()); if (NS_WARN_IF(!ownerElement)) { - return false; + // If this fails, the page was navigated. Fail the permission check by + // forcing an immediate retry. + return SendPermissionRetry(); } nsRefPtr<PermissionRequestMainProcessHelper> helper = new PermissionRequestMainProcessHelper(this, mFactory, ownerElement, principal); PermissionRequestBase::PermissionValue permission; if (NS_WARN_IF(NS_FAILED(helper->PromptIfNeeded(&permission)))) { return false;
--- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -98,21 +98,18 @@ public: // Return true if the media layer supports seeking. virtual bool IsTransportSeekable() = 0; // Return true if the transport layer supports seeking. virtual bool IsMediaSeekable() = 0; virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, MediaDecoderEventVisibility aEventVisibility) = 0; - virtual void QueueMetadata(const media::TimeUnit& aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) = 0; virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) = 0; - virtual void RemoveMediaTracks() = 0; - // May be called by the reader to notify this decoder that the metadata from // the media file has been read. Call on the decode thread only. virtual void OnReadMetadataCompleted() = 0; // Returns the owner of this media decoder. The owner should only be used // on the main thread. virtual MediaDecoderOwner* GetOwner() = 0; @@ -216,51 +213,12 @@ public: NS_IMETHOD Run() override { mDecoder->FirstFrameLoaded(mInfo, mEventVisibility); return NS_OK; } }; -class MetadataUpdatedEventRunner : public nsRunnable, private MetadataContainer -{ -public: - MetadataUpdatedEventRunner(AbstractMediaDecoder* aDecoder, - nsAutoPtr<MediaInfo> aInfo, - nsAutoPtr<MetadataTags> aTags, - MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable) - : MetadataContainer(aDecoder, aInfo, aTags, aEventVisibility) - {} - - NS_IMETHOD Run() override - { - nsAutoPtr<MediaInfo> info(new MediaInfo()); - *info = *mInfo; - mDecoder->MetadataLoaded(info, mTags, mEventVisibility); - mDecoder->FirstFrameLoaded(mInfo, mEventVisibility); - return NS_OK; - } -}; - -class RemoveMediaTracksEventRunner : public nsRunnable -{ -public: - explicit RemoveMediaTracksEventRunner(AbstractMediaDecoder* aDecoder) - : mDecoder(aDecoder) - {} - - NS_IMETHOD Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - - mDecoder->RemoveMediaTracks(); - return NS_OK; - } - -private: - nsRefPtr<AbstractMediaDecoder> mDecoder; -}; - } // namespace mozilla #endif
--- a/dom/media/MP3Demuxer.cpp +++ b/dom/media/MP3Demuxer.cpp @@ -396,20 +396,26 @@ MP3TrackDemuxer::FindNextFrame() { while (frameBeg == bufferEnd) { if ((!mParser.FirstFrame().Length() && mOffset - mParser.ID3Header().Size() > MAX_SKIPPED_BYTES) || (read = Read(buffer, mOffset, BUFFER_SIZE)) == 0) { // This is not a valid MPEG audio stream or we've reached EOS, give up. break; } - MOZ_ASSERT(mOffset + read > mOffset); + NS_ENSURE_TRUE(mOffset + read > mOffset, MediaByteRange(0, 0)); mOffset += read; bufferEnd = buffer + read; - frameBeg = mParser.Parse(buffer, bufferEnd); + const FrameParserResult parseResults = mParser.Parse(buffer, bufferEnd); + frameBeg = parseResults.mBufferPos; + + // If mBytesToSkip is > 0, this skips the rest of an ID3 tag which stretches + // beyond the current buffer. + NS_ENSURE_TRUE(mOffset + parseResults.mBytesToSkip >= mOffset, MediaByteRange(0, 0)); + mOffset += parseResults.mBytesToSkip; } if (frameBeg == bufferEnd || !mParser.CurrentFrame().Length()) { MP3DEMUXER_LOG("FindNext() Exit frameBeg=%p bufferEnd=%p " "mParser.CurrentFrame().Length()=%d ", frameBeg, bufferEnd, mParser.CurrentFrame().Length()); return { 0, 0 }; } @@ -600,47 +606,55 @@ FrameParser::ID3Header() const { return mID3Parser.Header(); } const FrameParser::VBRHeader& FrameParser::VBRInfo() const { return mVBRHeader; } -const uint8_t* +FrameParserResult FrameParser::Parse(const uint8_t* aBeg, const uint8_t* aEnd) { if (!aBeg || !aEnd || aBeg >= aEnd) { - return aEnd; + return { aEnd, 0 }; } if (!mID3Parser.Header().Size() && !mFirstFrame.Length()) { // No MP3 frames have been parsed yet, look for ID3v2 headers at file begin. // ID3v1 tags may only be at file end. // TODO: should we try to read ID3 tags at end of file/mid-stream, too? const uint8_t* id3Beg = mID3Parser.Parse(aBeg, aEnd); if (id3Beg != aEnd) { - // ID3 headers found, skip past them. - aBeg = id3Beg + ID3Parser::ID3Header::SIZE + mID3Parser.Header().Size(); + // ID3 tag found, skip past it. + const uint32_t tagSize = ID3Parser::ID3Header::SIZE + mID3Parser.Header().Size() + + mID3Parser.Header().FooterSize(); + const uint32_t remainingBuffer = aEnd - id3Beg; + if (tagSize > remainingBuffer) { + // Skipping across the ID3 tag would take us past the end of the buffer, therefore we + // return immediately and let the calling function handle skipping the rest of the tag. + return { aEnd, tagSize - remainingBuffer }; + } + aBeg = id3Beg + tagSize; } } while (aBeg < aEnd && !mFrame.ParseNext(*aBeg)) { ++aBeg; } if (mFrame.Length()) { // MP3 frame found. if (!mFirstFrame.Length()) { mFirstFrame = mFrame; } // Move to the frame header begin to allow for whole-frame parsing. aBeg -= FrameHeader::SIZE; - return aBeg; + return { aBeg, 0 }; } - return aEnd; + return { aEnd, 0 }; } // FrameParser::Header FrameParser::FrameHeader::FrameHeader() { Reset(); } @@ -785,29 +799,30 @@ FrameParser::FrameHeader::ParseNext(uint Reset(); } } return IsValid(); } bool FrameParser::FrameHeader::IsValid(int aPos) const { - if (IsValid()) { + if (aPos >= SIZE) { return true; } if (aPos == frame_header::SYNC1) { return Sync1() == 0xFF; } if (aPos == frame_header::SYNC2_VERSION_LAYER_PROTECTION) { return Sync2() == 7 && RawVersion() != 1 && RawLayer() != 0; } if (aPos == frame_header::BITRATE_SAMPLERATE_PADDING_PRIVATE) { - return RawBitrate() != 0xF; + return RawBitrate() != 0xF && RawBitrate() != 0 && + RawSampleRate() != 3; } return true; } bool FrameParser::FrameHeader::IsValid() const { return mPos >= SIZE; } @@ -1012,30 +1027,38 @@ ID3Parser::ID3Header::Flags() const { return mRaw[id3_header::FLAGS_END - id3_header::FLAGS_LEN]; } uint32_t ID3Parser::ID3Header::Size() const { return mSize; } +uint8_t +ID3Parser::ID3Header::FooterSize() const { + if (Flags() & (1 << 4)) { + return SIZE; + } + return 0; +} + bool ID3Parser::ID3Header::ParseNext(uint8_t c) { if (!Update(c)) { Reset(); if (!Update(c)) { Reset(); } } return IsValid(); } bool ID3Parser::ID3Header::IsValid(int aPos) const { - if (IsValid()) { + if (aPos >= SIZE) { return true; } const uint8_t c = mRaw[aPos]; switch (aPos) { case 0: case 1: case 2: // Expecting "ID3". return id3_header::ID[aPos] == c; case 3:
--- a/dom/media/MP3Demuxer.h +++ b/dom/media/MP3Demuxer.h @@ -56,19 +56,22 @@ public: // The ID3 tags are versioned like this: ID3vMajorVersion.MinorVersion. uint8_t MajorVersion() const; uint8_t MinorVersion() const; // The ID3 flags field. uint8_t Flags() const; - // The derived size based on the provides size fields. + // The derived size based on the provided size fields. uint32_t Size() const; + // Returns the size of an ID3v2.4 footer if present and zero otherwise. + uint8_t FooterSize() const; + // Returns whether the parsed data is a valid ID3 header up to the given // byte position. bool IsValid(int aPos) const; // Returns whether the parsed data is a complete and valid ID3 header. bool IsValid() const; // Parses the next provided byte. @@ -103,16 +106,21 @@ public: // Resets the state to allow for a new parsing session. void Reset(); private: // The currently parsed ID3 header. Reset via Reset, updated via Parse. ID3Header mHeader; }; +struct FrameParserResult { + const uint8_t* mBufferPos; + const uint32_t mBytesToSkip; +}; + // MPEG audio frame parser. // The MPEG frame header has the following format (one bit per character): // 11111111 111VVLLC BBBBSSPR MMEETOHH // { sync } - 11 sync bits // VV - MPEG audio version ID (0->2.5, 1->reserved, 2->2, 3->1) // LL - Layer description (0->reserved, 1->III, 2->II, 3->I) // C - CRC protection bit (0->protected, 1->not protected) // BBBB - Bitrate index (see table in implementation) @@ -277,19 +285,21 @@ public: void Reset(); // Clear the last parsed frame to allow for next frame parsing, i.e.: // - sets PrevFrame to CurrentFrame // - resets the CurrentFrame // - resets ID3Header if no valid header was parsed yet void EndFrameSession(); - // Parses given buffer [aBeg, aEnd) for a valid frame header. - // Returns begin of frame header if a frame header was found or aEnd otherwise. - const uint8_t* Parse(const uint8_t* aBeg, const uint8_t* aEnd); + // Parses given buffer [aBeg, aEnd) for a valid frame header and returns a FrameParserResult. + // FrameParserResult.mBufferPos points to begin of frame header if a frame header was found + // or to aEnd otherwise. FrameParserResult.mBytesToSkip indicates whether additional bytes need to + // be skipped in order to jump across an ID3 tag that stretches beyond the given buffer. + FrameParserResult Parse(const uint8_t* aBeg, const uint8_t* aEnd); // Parses given buffer [aBeg, aEnd) for a valid VBR header. // Returns whether a valid VBR header was found. bool ParseVBRHeader(const uint8_t* aBeg, const uint8_t* aEnd); private: // ID3 header parser. ID3Parser mID3Parser;
--- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -458,16 +458,17 @@ void MediaDecoder::Shutdown() mShuttingDown = true; // This changes the decoder state to SHUTDOWN and does other things // necessary to unblock the state machine thread if it's blocked, so // the asynchronous shutdown in nsDestroyStateMachine won't deadlock. if (mDecoderStateMachine) { mDecoderStateMachine->DispatchShutdown(); + mTimedMetadataListener.Disconnect(); } // Force any outstanding seek and byterange requests to complete // to prevent shutdown from deadlocking. if (mResource) { mResource->Close(); } @@ -540,16 +541,18 @@ nsresult MediaDecoder::InitializeStateMa } void MediaDecoder::SetStateMachineParameters() { MOZ_ASSERT(NS_IsMainThread()); if (mMinimizePreroll) { mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts(); } + mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect( + AbstractThread::MainThread(), this, &MediaDecoder::OnMetadataUpdate); } void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts() { MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()"); mMinimizePreroll = true; @@ -630,23 +633,25 @@ double MediaDecoder::GetCurrentTime() } already_AddRefed<nsIPrincipal> MediaDecoder::GetCurrentPrincipal() { MOZ_ASSERT(NS_IsMainThread()); return mResource ? mResource->GetCurrentPrincipal() : nullptr; } -void MediaDecoder::QueueMetadata(const TimeUnit& aPublishTime, - nsAutoPtr<MediaInfo> aInfo, - nsAutoPtr<MetadataTags> aTags) +void MediaDecoder::OnMetadataUpdate(TimedMetadata&& aMetadata) { - MOZ_ASSERT(OnDecodeTaskQueue()); - GetReentrantMonitor().AssertCurrentThreadIn(); - mDecoderStateMachine->QueueMetadata(aPublishTime, aInfo, aTags); + MOZ_ASSERT(NS_IsMainThread()); + RemoveMediaTracks(); + MetadataLoaded(nsAutoPtr<MediaInfo>(new MediaInfo(*aMetadata.mInfo)), + Move(aMetadata.mTags), + MediaDecoderEventVisibility::Observable); + FirstFrameLoaded(Move(aMetadata.mInfo), + MediaDecoderEventVisibility::Observable); } void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, MediaDecoderEventVisibility aEventVisibility) { MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -179,37 +179,40 @@ state machine once its threads are shutd The owning object of a MediaDecoder object *MUST* call Shutdown when destroying the MediaDecoder object. */ #if !defined(MediaDecoder_h_) #define MediaDecoder_h_ +#ifdef MOZ_EME +#include "mozilla/CDMProxy.h" +#endif + #include "mozilla/MozPromise.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/StateMirroring.h" #include "mozilla/StateWatching.h" #include "mozilla/dom/AudioChannelBinding.h" -#include "nsISupports.h" +#include "necko-config.h" +#include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsIObserver.h" -#include "nsAutoPtr.h" +#include "nsISupports.h" #include "nsITimer.h" -#include "MediaResource.h" -#include "MediaDecoderOwner.h" -#include "MediaStreamGraph.h" + #include "AbstractMediaDecoder.h" -#include "DecodedStream.h" -#include "necko-config.h" -#ifdef MOZ_EME -#include "mozilla/CDMProxy.h" -#endif +#include "MediaDecoderOwner.h" +#include "MediaEventSource.h" +#include "MediaMetadataManager.h" +#include "MediaResource.h" +#include "MediaStreamGraph.h" #include "TimeUnits.h" class nsIStreamListener; class nsIPrincipal; namespace mozilla { class VideoFrameContainer; @@ -574,23 +577,16 @@ public: // Returns true if we can play the entire media through without stopping // to buffer, given the current download and playback rates. bool CanPlayThrough(); void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; } dom::AudioChannel GetAudioChannel() { return mAudioChannel; } - // Send a new set of metadata to the state machine, to be dispatched to the - // main thread to be presented when the |currentTime| of the media is greater - // or equal to aPublishTime. - void QueueMetadata(const media::TimeUnit& aPublishTime, - nsAutoPtr<MediaInfo> aInfo, - nsAutoPtr<MetadataTags> aTags) override; - /****** * The following methods must only be called on the main * thread. ******/ // Change to a new play state. This updates the mState variable and // notifies any thread blocking on this object's monitor of the // change. Call on the main thread only. @@ -613,17 +609,17 @@ public: // Called from MetadataLoaded(). Creates audio tracks and adds them to its // owner's audio track list, and implies to video tracks respectively. // Call on the main thread only. void ConstructMediaTracks(); // Removes all audio tracks and video tracks that are previously added into // the track list. Call on the main thread only. - virtual void RemoveMediaTracks() override; + void RemoveMediaTracks(); // Called when the video has completed playing. // Call on the main thread only. void PlaybackEnded(); void OnSeekRejected() { MOZ_ASSERT(NS_IsMainThread()); @@ -984,16 +980,18 @@ protected: // Ensures our media stream has been pinned. void PinForSeek(); // Ensures our media stream has been unpinned. void UnpinForSeek(); const char* PlayStateStr(); + void OnMetadataUpdate(TimedMetadata&& aMetadata); + // This should only ever be accessed from the main thread. // It is set in Init and cleared in Shutdown when the element goes away. // The decoder does not add a reference the element. MediaDecoderOwner* mOwner; // Counters related to decode and presentation of frames. FrameStatistics mFrameStats; @@ -1054,16 +1052,19 @@ protected: const int mHeuristicDormantTimeout; // True if MediaDecoder is in dormant by heuristic. bool mIsHeuristicDormant; // Timer to schedule updating dormant state. nsCOMPtr<nsITimer> mDormantTimer; + // A listener to receive metadata updates from MDSM. + MediaEventListener mTimedMetadataListener; + protected: // Whether the state machine is shut down. Mirror<bool> mStateMachineIsShutdown; // Buffered range, mirrored from the reader. Mirror<media::TimeIntervals> mBuffered; // NextFrameStatus, mirrored from the state machine.
--- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -6,16 +6,17 @@ #if !defined(MediaDecoderReader_h_) #define MediaDecoderReader_h_ #include "mozilla/MozPromise.h" #include "AbstractMediaDecoder.h" #include "MediaInfo.h" #include "MediaData.h" +#include "MediaMetadataManager.h" #include "MediaQueue.h" #include "MediaTimer.h" #include "AudioCompactor.h" #include "Intervals.h" #include "TimeUnits.h" namespace mozilla { @@ -322,16 +323,20 @@ public: virtual bool IsAsync() const { return false; } // Returns true if this decoder reader uses hardware accelerated video // decoding. virtual bool VideoIsHardwareAccelerated() const { return false; } virtual void DisableHardwareAcceleration() {} + TimedMetadataEventSource& TimedMetadataEvent() { + return mTimedMetadataEvent; + } + protected: virtual ~MediaDecoderReader(); // Overrides of this function should decodes an unspecified amount of // audio data, enqueuing the audio data in mAudioQueue. Returns true // when there's more audio to decode, false if the audio is finished, // end of file has been reached, or an un-recoverable read error has // occured. This function blocks until the decode is complete. @@ -413,16 +418,19 @@ protected: // This is a quick-and-dirty way for DecodeAudioData implementations to // communicate the presence of a decoding error to RequestAudioData. We should // replace this with a promise-y mechanism as we make this stuff properly // async. bool mHitAudioDecodeError; bool mShutdown; + // Used to send TimedMetadata to the listener. + TimedMetadataEventProducer mTimedMetadataEvent; + private: // Promises used only for the base-class (sync->async adapter) implementation // of Request{Audio,Video}Data. MozPromiseHolder<AudioDataPromise> mBaseAudioPromise; MozPromiseHolder<VideoDataPromise> mBaseVideoPromise; bool mTaskQueueIsBorrowed;
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -11,16 +11,17 @@ #include "mozilla/DebugOnly.h" #include <stdint.h> #include "MediaDecoderStateMachine.h" #include "MediaTimer.h" #include "mediasink/DecodedAudioDataSink.h" #include "mediasink/AudioSinkWrapper.h" +#include "mediasink/DecodedStream.h" #include "nsTArray.h" #include "MediaDecoder.h" #include "MediaDecoderReader.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/mozalloc.h" #include "VideoUtils.h" #include "TimeUnits.h" #include "nsDeque.h" @@ -34,17 +35,16 @@ #include "mozilla/SharedThreadPool.h" #include "mozilla/TaskQueue.h" #include "nsIEventTarget.h" #include "prenv.h" #include "mozilla/Preferences.h" #include "gfx2DGlue.h" #include "nsPrintfCString.h" #include "DOMMediaStream.h" -#include "DecodedStream.h" #include "mozilla/Logging.h" #include <algorithm> namespace mozilla { using namespace mozilla::dom; using namespace mozilla::layers; @@ -216,17 +216,17 @@ MediaDecoderStateMachine::MediaDecoderSt mDropVideoUntilNextDiscontinuity(false), mDecodeToSeekTarget(false), mCurrentTimeBeforeSeek(0), mCorruptFrames(60), mDecodingFirstFrame(true), mSentLoadedMetadataEvent(false), mSentFirstFrameLoadedEvent(false), mSentPlaybackEndedEvent(false), - mDecodedStream(new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue)), + mStreamSink(new DecodedStream(mTaskQueue, mAudioQueue, mVideoQueue)), mResource(aDecoder->GetResource()), mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderStateMachine::mBuffered (Mirror)"), mEstimatedDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderStateMachine::mEstimatedDuration (Mirror)"), mExplicitDuration(mTaskQueue, Maybe<double>(), "MediaDecoderStateMachine::mExplicitDuration (Mirror)"), mPlayState(mTaskQueue, MediaDecoder::PLAY_STATE_LOADING, @@ -284,16 +284,18 @@ MediaDecoderStateMachine::MediaDecoderSt timeBeginPeriod(1); #endif mAudioQueueListener = AudioQueue().PopEvent().Connect( mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped); mVideoQueueListener = VideoQueue().PopEvent().Connect( mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped); + mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread()); + nsRefPtr<MediaDecoderStateMachine> self = this; auto audioSinkCreator = [self] () { MOZ_ASSERT(self->OnTaskQueue()); return new DecodedAudioDataSink( self->mAudioQueue, self->GetMediaTime(), self->mInfo.mAudio, self->mDecoder->GetAudioChannel()); }; mAudioSink = new AudioSinkWrapper(mTaskQueue, audioSinkCreator); @@ -1094,17 +1096,17 @@ void MediaDecoderStateMachine::UpdatePla } void MediaDecoderStateMachine::UpdatePlaybackPosition(int64_t aTime) { MOZ_ASSERT(OnTaskQueue()); UpdatePlaybackPositionInternal(aTime); bool fragmentEnded = mFragmentEndTime >= 0 && GetMediaTime() >= mFragmentEndTime; - mMetadataManager.DispatchMetadataIfNeeded(mDecoder, TimeUnit::FromMicroseconds(aTime)); + mMetadataManager.DispatchMetadataIfNeeded(TimeUnit::FromMicroseconds(aTime)); if (fragmentEnded) { StopPlayback(); } } void MediaDecoderStateMachine::ClearPositionChangeFlag() { @@ -1146,17 +1148,17 @@ void MediaDecoderStateMachine::SetState( mSentPlaybackEndedEvent = false; } void MediaDecoderStateMachine::VolumeChanged() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mAudioSink->SetVolume(mVolume); - mDecodedStream->SetVolume(mVolume); + mStreamSink->SetVolume(mVolume); } void MediaDecoderStateMachine::RecomputeDuration() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); TimeUnit duration; @@ -1400,17 +1402,17 @@ void MediaDecoderStateMachine::Logically ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); ScheduleStateMachine(); } void MediaDecoderStateMachine::SameOriginMediaChanged() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - mDecodedStream->SetSameOrigin(mSameOriginMedia); + mStreamSink->SetSameOrigin(mSameOriginMedia); } void MediaDecoderStateMachine::BufferedRangeUpdated() { MOZ_ASSERT(OnTaskQueue()); // While playing an unseekable stream of unknown duration, mObservedDuration // is updated (in AdvanceFrame()) as we play. But if data is being downloaded @@ -1754,48 +1756,54 @@ MediaDecoderStateMachine::StartAudioSink { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); if (mAudioCaptured) { MOZ_ASSERT(!mAudioSink->IsStarted()); return; } - if (HasAudio() && !mAudioSink->IsStarted()) { + if (!mAudioSink->IsStarted()) { mAudioCompleted = false; mAudioSink->Start(GetMediaTime(), mInfo); - mAudioSinkPromise.Begin( - mAudioSink->OnEnded(TrackInfo::kAudioTrack)->Then( + auto promise = mAudioSink->OnEnded(TrackInfo::kAudioTrack); + if (promise) { + mAudioSinkPromise.Begin(promise->Then( OwnerThread(), __func__, this, &MediaDecoderStateMachine::OnAudioSinkComplete, &MediaDecoderStateMachine::OnAudioSinkError)); + } } } void MediaDecoderStateMachine::StopDecodedStream() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); - mDecodedStream->StopPlayback(); - mDecodedStreamPromise.DisconnectIfExists(); + + if (mStreamSink->IsStarted()) { + mStreamSink->Stop(); + mDecodedStreamPromise.DisconnectIfExists(); + } } void MediaDecoderStateMachine::StartDecodedStream() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); // Tell DecodedStream to start playback with specified start time and media // info. This is consistent with how we create AudioSink in StartAudioThread(). - if (mAudioCaptured && !mDecodedStreamPromise.Exists()) { + if (mAudioCaptured && !mStreamSink->IsStarted()) { + mStreamSink->Start(GetMediaTime(), mInfo); mDecodedStreamPromise.Begin( - mDecodedStream->StartPlayback(GetMediaTime(), mInfo)->Then( + mStreamSink->OnEnded(TrackInfo::kAudioTrack)->Then( OwnerThread(), __func__, this, &MediaDecoderStateMachine::OnDecodedStreamFinish, &MediaDecoderStateMachine::OnDecodedStreamError)); } } int64_t MediaDecoderStateMachine::AudioDecodedUsecs() { @@ -2178,28 +2186,38 @@ public: private: virtual ~DecoderDisposer() {} nsRefPtr<MediaDecoder> mDecoder; nsRefPtr<MediaDecoderStateMachine> mStateMachine; }; void +MediaDecoderStateMachine::DispatchShutdown() +{ + mStreamSink->BeginShutdown(); + nsCOMPtr<nsIRunnable> runnable = + NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown); + OwnerThread()->Dispatch(runnable.forget()); +} + +void MediaDecoderStateMachine::FinishShutdown() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); // The reader's listeners hold references to the state machine, // creating a cycle which keeps the state machine and its shared // thread pools alive. So break it here. // Prevent dangling pointers by disconnecting the listeners. mAudioQueueListener.Disconnect(); mVideoQueueListener.Disconnect(); + mMetadataManager.Disconnect(); // Disconnect canonicals and mirrors before shutting down our task queue. mBuffered.DisconnectIfConnected(); mEstimatedDuration.DisconnectIfConnected(); mExplicitDuration.DisconnectIfConnected(); mPlayState.DisconnectIfConnected(); mNextPlayState.DisconnectIfConnected(); mLogicallySeeking.DisconnectIfConnected(); @@ -2356,17 +2374,17 @@ nsresult MediaDecoderStateMachine::RunSt if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING && IsPlaying()) { StopPlayback(); } // Play the remaining media. We want to run AdvanceFrame() at least // once to ensure the current playback position is advanced to the // end of the media, and so that we update the readyState. if (VideoQueue().GetSize() > 1 || (HasAudio() && !mAudioCompleted) || - (mAudioCaptured && !mDecodedStream->IsFinished())) + (mAudioCaptured && !mStreamSink->IsFinished())) { // Start playback if necessary to play the remaining media. MaybeStartPlayback(); UpdateRenderedVideoFrames(); NS_ASSERTION(!IsPlaying() || mLogicallySeeking || IsStateMachineScheduled(), "Must have timer scheduled"); @@ -2548,86 +2566,34 @@ void MediaDecoderStateMachine::RenderVid frame->mTime, frame->mFrameID, VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(), VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames()); } container->SetCurrentFrames(frames[0]->As<VideoData>()->mDisplay, images); } -void MediaDecoderStateMachine::ResyncAudioClock() -{ - MOZ_ASSERT(OnTaskQueue()); - AssertCurrentThreadInMonitor(); - if (IsPlaying()) { - SetPlayStartTime(TimeStamp::Now()); - mPlayDuration = GetAudioClock(); - } -} - -int64_t -MediaDecoderStateMachine::GetAudioClock() const -{ - MOZ_ASSERT(OnTaskQueue()); - // We must hold the decoder monitor while using the audio stream off the - // audio sink to ensure that it doesn't get destroyed on the audio sink - // while we're using it. - AssertCurrentThreadInMonitor(); - MOZ_ASSERT(HasAudio() && !mAudioCompleted && IsPlaying()); - // Since this function is called while we are playing and AudioSink is - // started once playback starts, IsStarted() is guaranteed to be true. - MOZ_ASSERT(mAudioSink->IsStarted()); - return mAudioSink->GetPosition(); -} - -int64_t MediaDecoderStateMachine::GetStreamClock() const -{ - MOZ_ASSERT(OnTaskQueue()); - AssertCurrentThreadInMonitor(); - return mDecodedStream->GetPosition(); -} - -int64_t MediaDecoderStateMachine::GetVideoStreamPosition(TimeStamp aTimeStamp) const -{ - MOZ_ASSERT(OnTaskQueue()); - AssertCurrentThreadInMonitor(); - - if (!IsPlaying()) { - return mPlayDuration; - } - - // Time elapsed since we started playing. - int64_t delta = DurationToUsecs(aTimeStamp - mPlayStartTime); - // Take playback rate into account. - delta *= mPlaybackRate; - return mPlayDuration + delta; -} - int64_t MediaDecoderStateMachine::GetClock(TimeStamp* aTimeStamp) const { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); // Determine the clock time. If we've got audio, and we've not reached // the end of the audio, use the audio clock. However if we've finished // audio, or don't have audio, use the system clock. If our output is being // fed to a MediaStream, use that stream as the source of the clock. int64_t clock_time = -1; TimeStamp t; if (!IsPlaying()) { clock_time = mPlayDuration; } else { if (mAudioCaptured) { - clock_time = GetStreamClock(); - } else if (HasAudio() && !mAudioCompleted) { - clock_time = GetAudioClock(); + clock_time = mStreamSink->GetPosition(&t); } else { - t = TimeStamp::Now(); - // Audio is disabled on this system. Sync to the system clock. - clock_time = GetVideoStreamPosition(t); + clock_time = mAudioSink->GetPosition(&t); } NS_ASSERTION(GetMediaTime() <= clock_time, "Clock should go forwards."); } if (aTimeStamp) { *aTimeStamp = t.IsNull() ? TimeStamp::Now() : t; } return clock_time; @@ -2910,20 +2876,17 @@ void MediaDecoderStateMachine::StartBuff void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp) { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); mPlayStartTime = aTimeStamp; mAudioSink->SetPlaying(!mPlayStartTime.IsNull()); - // Have DecodedStream remember the playing state so it doesn't need to - // ask MDSM about IsPlaying(). Note we have to do this even before capture - // happens since capture could happen in the middle of playback. - mDecodedStream->SetPlaying(!mPlayStartTime.IsNull()); + mStreamSink->SetPlaying(!mPlayStartTime.IsNull()); } void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); DispatchAudioDecodeTaskIfNeeded(); DispatchVideoDecodeTaskIfNeeded(); @@ -2990,27 +2953,16 @@ MediaDecoderStateMachine::LogicalPlaybac MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); if (mLogicalPlaybackRate == 0) { // This case is handled in MediaDecoder by pausing playback. return; } - // AudioStream will handle playback rate change when we have audio. - // Do nothing while we are not playing. Change in playback rate will - // take effect next time we start playing again. - if (!HasAudio() && IsPlaying()) { - // Remember how much time we've spent in playing the media - // for playback rate will change from now on. - TimeStamp now = TimeStamp::Now(); - mPlayDuration = GetVideoStreamPosition(now); - SetPlayStartTime(now); - } - mPlaybackRate = mLogicalPlaybackRate; mAudioSink->SetPlaybackRate(mPlaybackRate); ScheduleStateMachine(); } void MediaDecoderStateMachine::PreservesPitchChanged() { @@ -3020,62 +2972,47 @@ void MediaDecoderStateMachine::Preserves } bool MediaDecoderStateMachine::IsShutdown() { MOZ_ASSERT(OnTaskQueue()); return mIsShutdown; } -void MediaDecoderStateMachine::QueueMetadata(const TimeUnit& aPublishTime, - nsAutoPtr<MediaInfo> aInfo, - nsAutoPtr<MetadataTags> aTags) -{ - MOZ_ASSERT(OnDecodeTaskQueue()); - AssertCurrentThreadInMonitor(); - TimedMetadata* metadata = new TimedMetadata; - metadata->mPublishTime = aPublishTime; - metadata->mInfo = aInfo.forget(); - metadata->mTags = aTags.forget(); - mMetadataManager.QueueMetadata(metadata); -} - int64_t MediaDecoderStateMachine::AudioEndTime() const { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); if (mAudioSink->IsStarted()) { return mAudioSink->GetEndTime(TrackInfo::kAudioTrack); } else if (mAudioCaptured) { - return mDecodedStream->AudioEndTime(); + return mStreamSink->GetEndTime(TrackInfo::kAudioTrack); } MOZ_ASSERT(!HasAudio()); return -1; } void MediaDecoderStateMachine::OnAudioSinkComplete() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio."); mAudioSinkPromise.Complete(); - ResyncAudioClock(); mAudioCompleted = true; } void MediaDecoderStateMachine::OnAudioSinkError() { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio."); mAudioSinkPromise.Complete(); - ResyncAudioClock(); mAudioCompleted = true; // Make the best effort to continue playback when there is video. if (HasVideo()) { return; } // Otherwise notify media decoder/element about this error for it makes @@ -3160,26 +3097,26 @@ void MediaDecoderStateMachine::DispatchA OwnerThread()->Dispatch(r.forget()); } void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded) { MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("AddOutputStream aStream=%p!", aStream); - mDecodedStream->AddOutput(aStream, aFinishWhenEnded); + mStreamSink->AddOutput(aStream, aFinishWhenEnded); DispatchAudioCaptured(); } void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream) { MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG("RemoveOutputStream=%p!", aStream); - mDecodedStream->RemoveOutput(aStream); - if (!mDecodedStream->HasConsumers()) { + mStreamSink->RemoveOutput(aStream); + if (!mStreamSink->HasConsumers()) { DispatchAudioUncaptured(); } } } // namespace mozilla // avoid redefined macro in unified build #undef LOG
--- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -89,26 +89,26 @@ hardware (via AudioStream). #include "nsThreadUtils.h" #include "MediaDecoder.h" #include "MediaDecoderReader.h" #include "MediaDecoderOwner.h" #include "MediaEventSource.h" #include "MediaMetadataManager.h" #include "MediaTimer.h" -#include "DecodedStream.h" #include "ImageContainer.h" namespace mozilla { namespace media { class MediaSink; } class AudioSegment; +class DecodedStream; class TaskQueue; extern PRLogModuleInfo* gMediaDecoderLog; extern PRLogModuleInfo* gMediaSampleLog; /* The state machine class. This manages the decoding and seeking in the MediaDecoderReader on the decode task queue, and A/V sync on the shared @@ -152,35 +152,33 @@ public: void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded); // Remove an output stream added with AddOutputStream. void RemoveOutputStream(MediaStream* aStream); // Set/Unset dormant state. void SetDormant(bool aDormant); + TimedMetadataEventSource& TimedMetadataEvent() { + return mMetadataManager.TimedMetadataEvent(); + } + private: // Initialization that needs to happen on the task queue. This is the first // task that gets run on the task queue, and is dispatched from the MDSM // constructor immediately after the task queue is created. void InitializationTask(); void DispatchAudioCaptured(); void DispatchAudioUncaptured(); void Shutdown(); public: - void DispatchShutdown() - { - mDecodedStream->Shutdown(); - nsCOMPtr<nsIRunnable> runnable = - NS_NewRunnableMethod(this, &MediaDecoderStateMachine::Shutdown); - OwnerThread()->Dispatch(runnable.forget()); - } + void DispatchShutdown(); void FinishShutdown(); // Immutable after construction - may be called on any thread. bool IsRealTime() const { return mRealTime; } // Functions used by assertions to ensure we're calling things // on the appropriate threads. @@ -328,20 +326,16 @@ public: void DiscardStreamData(); bool HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs); bool HaveEnoughDecodedVideo(); // Returns true if the state machine has shutdown or is in the process of // shutting down. The decoder monitor must be held while calling this. bool IsShutdown(); - void QueueMetadata(const media::TimeUnit& aPublishTime, - nsAutoPtr<MediaInfo> aInfo, - nsAutoPtr<MetadataTags> aTags); - // Returns true if we're currently playing. The decoder monitor must // be held. bool IsPlaying() const; // Called when the reader may have acquired the hardware resources required // to begin decoding. void NotifyWaitingForResourcesStatusChanged(); @@ -452,32 +446,16 @@ protected: // Returns true if we recently exited "quick buffering" mode. bool JustExitedQuickBuffering(); // Recomputes mNextFrameStatus, possibly dispatching notifications to interested // parties. void UpdateNextFrameStatus(); - // Called when AudioSink reaches the end. |mPlayStartTime| and - // |mPlayDuration| are updated to provide a good base for calculating video - // stream time. - void ResyncAudioClock(); - - // Returns the audio clock, if we have audio, or -1 if we don't. - // Called on the state machine thread. - int64_t GetAudioClock() const; - - int64_t GetStreamClock() const; - - // Get the video stream position, taking the |playbackRate| change into - // account. This is a position in the media, not the duration of the playback - // so far. Returns the position for the given time aTimeStamp. - int64_t GetVideoStreamPosition(TimeStamp aTimeStamp) const; - // Return the current time, either the audio clock if available (if the media // has audio, and the playback is possible), or a clock for the video. // Called on the state machine thread. // If aTimeStamp is non-null, set *aTimeStamp to the TimeStamp corresponding // to the returned stream time. int64_t GetClock(TimeStamp* aTimeStamp = nullptr) const; nsresult DropAudioUpToSeekTarget(AudioData* aSample); @@ -1282,17 +1260,17 @@ private: bool mSentPlaybackEndedEvent; // The SourceMediaStream we are using to feed the mOutputStreams. This stream // is never exposed outside the decoder. // Only written on the main thread while holding the monitor. Therefore it // can be read on any thread while holding the monitor, or on the main thread // without holding the monitor. - nsRefPtr<DecodedStream> mDecodedStream; + nsRefPtr<DecodedStream> mStreamSink; // Media data resource from the decoder. nsRefPtr<MediaResource> mResource; MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise; MozPromiseRequestHolder<GenericPromise> mDecodedStreamPromise; MediaEventListener mAudioQueueListener;
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -1373,16 +1373,18 @@ MediaManager::IsInMediaThread() (sSingleton->mMediaThread->thread_id() == PlatformThread::CurrentId()) : false; } #endif // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread // from MediaManager thread. + +// Guaranteed never to return nullptr. /* static */ MediaManager* MediaManager::Get() { if (!sSingleton) { NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread"); #ifdef DEBUG static int timesCreated = 0; timesCreated++; MOZ_ASSERT(timesCreated == 1); @@ -2061,29 +2063,40 @@ MediaManager::EnumerateDevices(nsPIDOMWi nsIDOMGetUserMediaErrorCallback* aOnFailure) { MOZ_ASSERT(NS_IsMainThread()); NS_ENSURE_TRUE(!sInShutdown, NS_ERROR_FAILURE); nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess); nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure); uint64_t windowId = aWindow->WindowID(); - AddWindowID(windowId); + StreamListeners* listeners = AddWindowID(windowId); + + // Create a disabled listener to act as a placeholder + nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener = + new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowId); + + // No need for locking because we always do this in the main thread. + listeners->AppendElement(listener); bool fake = Preferences::GetBool("media.navigator.streams.fake"); nsRefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId, dom::MediaSourceEnum::Camera, dom::MediaSourceEnum::Microphone, fake); - p->Then([onSuccess](SourceSet*& aDevices) mutable { + p->Then([onSuccess, windowId, listener](SourceSet*& aDevices) mutable { ScopedDeletePtr<SourceSet> devices(aDevices); // grab result + nsRefPtr<MediaManager> mgr = MediaManager_GetInstance(); + mgr->RemoveFromWindowList(windowId, listener); nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices); onSuccess->OnSuccess(array); - }, [onFailure](MediaStreamError& reason) mutable { + }, [onFailure, windowId, listener](MediaStreamError& reason) mutable { + nsRefPtr<MediaManager> mgr = MediaManager_GetInstance(); + mgr->RemoveFromWindowList(windowId, listener); onFailure->OnError(&reason); }); return NS_OK; } /* * GetUserMediaDevices - called by the UI-part of getUserMedia from chrome JS. */
--- a/dom/media/MediaMetadataManager.h +++ b/dom/media/MediaMetadataManager.h @@ -2,73 +2,105 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ #if !defined(MediaMetadataManager_h__) #define MediaMetadataManager_h__ +#include "mozilla/AbstractThread.h" #include "mozilla/LinkedList.h" #include "nsAutoPtr.h" #include "AbstractMediaDecoder.h" +#include "MediaEventSource.h" #include "TimeUnits.h" #include "VideoUtils.h" namespace mozilla { +class TimedMetadata; +typedef MediaEventProducer<TimedMetadata, ListenerMode::Exclusive> + TimedMetadataEventProducer; +typedef MediaEventSource<TimedMetadata, ListenerMode::Exclusive> + TimedMetadataEventSource; + // A struct that contains the metadata of a media, and the time at which those // metadata should start to be reported. class TimedMetadata : public LinkedListElement<TimedMetadata> { public: + TimedMetadata(const media::TimeUnit& aPublishTime, + nsAutoPtr<MetadataTags>&& aTags, + nsAutoPtr<MediaInfo>&& aInfo) + : mPublishTime(aPublishTime) + , mTags(Move(aTags)) + , mInfo(Move(aInfo)) {} + + // Define our move constructor because we don't want to move the members of + // LinkedListElement to change the list. + TimedMetadata(TimedMetadata&& aOther) + : mPublishTime(aOther.mPublishTime) + , mTags(Move(aOther.mTags)) + , mInfo(Move(aOther.mInfo)) {} + // The time, in microseconds, at which those metadata should be available. media::TimeUnit mPublishTime; // The metadata. The ownership is transfered to the element when dispatching to // the main threads. nsAutoPtr<MetadataTags> mTags; // The media info, including the info of audio tracks and video tracks. // The ownership is transfered to MediaDecoder when dispatching to the // main thread. nsAutoPtr<MediaInfo> mInfo; }; // This class encapsulate the logic to give the metadata from the reader to // the content, at the right time. -class MediaMetadataManager -{ +class MediaMetadataManager { public: ~MediaMetadataManager() { TimedMetadata* element; while((element = mMetadataQueue.popFirst()) != nullptr) { delete element; } } - void QueueMetadata(TimedMetadata* aMetadata) { - mMetadataQueue.insertBack(aMetadata); + // Connect to an event source to receive TimedMetadata events. + void Connect(TimedMetadataEventSource& aEvent, AbstractThread* aThread) { + mListener = aEvent.Connect( + aThread, this, &MediaMetadataManager::OnMetadataQueued); } - void DispatchMetadataIfNeeded(AbstractMediaDecoder* aDecoder, const media::TimeUnit& aCurrentTime) { + // Stop receiving TimedMetadata events. + void Disconnect() { + mListener.Disconnect(); + } + + // Return an event source through which we will send TimedMetadata events + // when playback position reaches the publish time. + TimedMetadataEventSource& TimedMetadataEvent() { + return mTimedMetadataEvent; + } + + void DispatchMetadataIfNeeded(const media::TimeUnit& aCurrentTime) { TimedMetadata* metadata = mMetadataQueue.getFirst(); while (metadata && aCurrentTime >= metadata->mPublishTime) { - // Remove all media tracks from the list first. - nsCOMPtr<nsIRunnable> removeTracksEvent = - new RemoveMediaTracksEventRunner(aDecoder); - NS_DispatchToMainThread(removeTracksEvent); - - nsCOMPtr<nsIRunnable> metadataUpdatedEvent = - new MetadataUpdatedEventRunner(aDecoder, - metadata->mInfo, - metadata->mTags); - NS_DispatchToMainThread(metadataUpdatedEvent); + // Our listener will figure out what to do with TimedMetadata. + mTimedMetadataEvent.Notify(Move(*metadata)); delete mMetadataQueue.popFirst(); metadata = mMetadataQueue.getFirst(); } } protected: + void OnMetadataQueued(TimedMetadata&& aMetadata) { + mMetadataQueue.insertBack(new TimedMetadata(Move(aMetadata))); + } + LinkedList<TimedMetadata> mMetadataQueue; + MediaEventListener mListener; + TimedMetadataEventProducer mTimedMetadataEvent; }; } // namespace mozilla #endif
--- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -536,17 +536,17 @@ private: MediaStreamGraph* gm = mRecorder->GetSourceMediaStream()->Graph(); mTrackUnionStream = gm->CreateTrackUnionStream(nullptr); MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed"); mTrackUnionStream->SetAutofinish(true); // Bind this Track Union Stream with Source Media. mInputPort = mTrackUnionStream->AllocateInputPort(mRecorder->GetSourceMediaStream(), - MediaInputPort::FLAG_BLOCK_OUTPUT); + 0); DOMMediaStream* domStream = mRecorder->Stream(); if (domStream) { // Get the track type hint from DOM media stream. TracksAvailableCallback* tracksAvailableCallback = new TracksAvailableCallback(this); domStream->OnTracksAvailable(tracksAvailableCallback); } else { // Web Audio node has only audio. @@ -787,17 +787,17 @@ MediaRecorder::MediaRecorder(AudioNode& AudioNodeEngine* engine = new AudioNodeEngine(nullptr); AudioNodeStream::Flags flags = AudioNodeStream::EXTERNAL_OUTPUT | AudioNodeStream::NEED_MAIN_THREAD_FINISHED; mPipeStream = AudioNodeStream::Create(ctx->Graph(), engine, flags); AudioNodeStream* ns = aSrcAudioNode.GetStream(); if (ns) { mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(), - MediaInputPort::FLAG_BLOCK_INPUT, + 0, 0, aSrcOutput); } } mAudioNode = &aSrcAudioNode; if (!gMediaRecorderLog) { gMediaRecorderLog = PR_NewLogModule("MediaRecorder"); }
--- a/dom/media/MediaResource.h +++ b/dom/media/MediaResource.h @@ -20,17 +20,21 @@ #include "mozilla/Attributes.h" #include "mozilla/TimeStamp.h" #include "nsThreadUtils.h" #include <algorithm> // For HTTP seeking, if number of bytes needing to be // seeked forward is less than this value then a read is // done rather than a byte range request. -static const int64_t SEEK_VS_READ_THRESHOLD = 32*1024; +// +// If we assume a 100Mbit connection, and assume reissuing an HTTP seek causes +// a delay of 200ms, then in that 200ms we could have simply read ahead 2MB. So +// setting SEEK_VS_READ_THRESHOLD to 1MB sounds reasonable. +static const int64_t SEEK_VS_READ_THRESHOLD = 1 * 1024 * 1024; static const uint32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE = 416; // Number of bytes we have accumulated before we assume the connection download // rate can be reliably calculated. 57 Segments at IW=3 allows slow start to // reach a CWND of 30 (See bug 831998) static const int64_t RELIABLE_DATA_THRESHOLD = 57 * 1460;
--- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -77,38 +77,35 @@ MediaStreamGraphImpl::FinishStream(Media return; STREAM_LOG(LogLevel::Debug, ("MediaStream %p will finish", aStream)); aStream->mFinished = true; aStream->mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX); SetStreamOrderDirty(); } -static const GraphTime START_TIME_DELAYED = -1; - void MediaStreamGraphImpl::AddStreamGraphThread(MediaStream* aStream) { + aStream->mBufferStartTime = mProcessedTime; // Check if we're adding a stream to a suspended context, in which case, we - // add it to mSuspendedStreams, and delay setting mBufferStartTime + // add it to mSuspendedStreams bool contextSuspended = false; if (aStream->AsAudioNodeStream()) { for (uint32_t i = 0; i < mSuspendedStreams.Length(); i++) { if (aStream->AudioContextId() == mSuspendedStreams[i]->AudioContextId()) { contextSuspended = true; } } } if (contextSuspended) { - aStream->mBufferStartTime = START_TIME_DELAYED; mSuspendedStreams.AppendElement(aStream); STREAM_LOG(LogLevel::Debug, ("Adding media stream %p to the graph, in the suspended stream array", aStream)); } else { - aStream->mBufferStartTime = mProcessedTime; mStreams.AppendElement(aStream); STREAM_LOG(LogLevel::Debug, ("Adding media stream %p to the graph", aStream)); } SetStreamOrderDirty(); } void @@ -354,71 +351,62 @@ MediaStreamGraphImpl::StreamReadyToFinis } } } void MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime, GraphTime aNextCurrentTime) { - nsTArray<MediaStream*>* runningAndSuspendedPair[2]; - runningAndSuspendedPair[0] = &mStreams; - runningAndSuspendedPair[1] = &mSuspendedStreams; - - for (uint32_t array = 0; array < 2; array++) { - for (uint32_t i = 0; i < runningAndSuspendedPair[array]->Length(); ++i) { - MediaStream* stream = (*runningAndSuspendedPair[array])[i]; - - // Calculate blocked time and fire Blocked/Unblocked events - GraphTime blockedTime = 0; - GraphTime t = aPrevCurrentTime; - // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called - // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == - // stream end time| - while (t <= aNextCurrentTime) { - GraphTime end; - bool blocked = stream->mBlocked.GetAt(t, &end); - if (blocked) { - blockedTime += std::min(end, aNextCurrentTime) - t; + for (MediaStream* stream : AllStreams()) { + // Calculate blocked time and fire Blocked/Unblocked events + GraphTime blockedTime = 0; + GraphTime t = aPrevCurrentTime; + // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called + // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == + // stream end time| + while (t <= aNextCurrentTime) { + GraphTime end; + bool blocked = stream->mBlocked.GetAt(t, &end); + if (blocked) { + blockedTime += std::min(end, aNextCurrentTime) - t; + } + if (blocked != stream->mNotifiedBlocked) { + for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) { + MediaStreamListener* l = stream->mListeners[j]; + l->NotifyBlockingChanged(this, blocked + ? MediaStreamListener::BLOCKED + : MediaStreamListener::UNBLOCKED); } - if (blocked != stream->mNotifiedBlocked) { - for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) { - MediaStreamListener* l = stream->mListeners[j]; - l->NotifyBlockingChanged(this, blocked - ? MediaStreamListener::BLOCKED - : MediaStreamListener::UNBLOCKED); - } - stream->mNotifiedBlocked = blocked; - } - t = end; + stream->mNotifiedBlocked = blocked; } - - stream->AdvanceTimeVaryingValuesToCurrentTime(aNextCurrentTime, - blockedTime); - // Advance mBlocked last so that AdvanceTimeVaryingValuesToCurrentTime - // can rely on the value of mBlocked. - stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime); - - if (runningAndSuspendedPair[array] == &mStreams) { - bool streamHasOutput = blockedTime < aNextCurrentTime - aPrevCurrentTime; - NS_ASSERTION(!streamHasOutput || !stream->mNotifiedFinished, - "Shouldn't have already notified of finish *and* have output!"); - - if (streamHasOutput) { - StreamNotifyOutput(stream); - } - - if (stream->mFinished && !stream->mNotifiedFinished) { - StreamReadyToFinish(stream); - } - } - STREAM_LOG(LogLevel::Verbose, - ("MediaStream %p bufferStartTime=%f blockedTime=%f", stream, - MediaTimeToSeconds(stream->mBufferStartTime), - MediaTimeToSeconds(blockedTime))); + t = end; + } + + stream->AdvanceTimeVaryingValuesToCurrentTime(aNextCurrentTime, + blockedTime); + // Advance mBlocked last so that AdvanceTimeVaryingValuesToCurrentTime + // can rely on the value of mBlocked. + stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime); + + STREAM_LOG(LogLevel::Verbose, + ("MediaStream %p bufferStartTime=%f blockedTime=%f", stream, + MediaTimeToSeconds(stream->mBufferStartTime), + MediaTimeToSeconds(blockedTime))); + + bool streamHasOutput = blockedTime < aNextCurrentTime - aPrevCurrentTime; + NS_ASSERTION(!streamHasOutput || !stream->mNotifiedFinished, + "Shouldn't have already notified of finish *and* have output!"); + + if (streamHasOutput) { + StreamNotifyOutput(stream); + } + + if (stream->mFinished && !stream->mNotifiedFinished) { + StreamReadyToFinish(stream); } } } bool MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime, GraphTime aEndBlockingDecisions, GraphTime* aEnd) { @@ -737,36 +725,29 @@ MediaStreamGraphImpl::UpdateStreamOrder( MOZ_ASSERT(orderedStreamCount == mFirstCycleBreaker); } void MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions) { STREAM_LOG(LogLevel::Verbose, ("Media graph %p computing blocking for time %f", this, MediaTimeToSeconds(mStateComputedTime))); - nsTArray<MediaStream*>* runningAndSuspendedPair[2]; - runningAndSuspendedPair[0] = &mStreams; - runningAndSuspendedPair[1] = &mSuspendedStreams; - - for (uint32_t array = 0; array < 2; array++) { - for (uint32_t i = 0; i < (*runningAndSuspendedPair[array]).Length(); ++i) { - MediaStream* stream = (*runningAndSuspendedPair[array])[i]; - if (!stream->mInBlockingSet) { - // Compute a partition of the streams containing 'stream' such that we - // can - // compute the blocking status of each subset independently. - nsAutoTArray<MediaStream*, 10> streamSet; - AddBlockingRelatedStreamsToSet(&streamSet, stream); - - GraphTime end; - for (GraphTime t = mStateComputedTime; - t < aEndBlockingDecisions; t = end) { - end = GRAPH_TIME_MAX; - RecomputeBlockingAt(streamSet, t, aEndBlockingDecisions, &end); - } + for (MediaStream* stream : AllStreams()) { + if (!stream->mInBlockingSet) { + // Compute a partition of the streams containing 'stream' such that we + // can + // compute the blocking status of each subset independently. + nsAutoTArray<MediaStream*, 10> streamSet; + AddBlockingRelatedStreamsToSet(&streamSet, stream); + + GraphTime end; + for (GraphTime t = mStateComputedTime; + t < aEndBlockingDecisions; t = end) { + end = GRAPH_TIME_MAX; + RecomputeBlockingAt(streamSet, t, aEndBlockingDecisions, &end); } } } STREAM_LOG(LogLevel::Verbose, ("Media graph %p computed blocking for interval %f to %f", this, MediaTimeToSeconds(mStateComputedTime), MediaTimeToSeconds(aEndBlockingDecisions))); MOZ_ASSERT(aEndBlockingDecisions >= mProcessedTime); @@ -860,16 +841,22 @@ MediaStreamGraphImpl::RecomputeBlockingA bool explicitBlock = stream->mExplicitBlockerCount.GetAt(aTime, &end) > 0; *aEnd = std::min(*aEnd, end); if (explicitBlock) { STREAM_LOG(LogLevel::Verbose, ("MediaStream %p is blocked due to explicit blocker", stream)); MarkStreamBlocking(stream); continue; } + if (StreamSuspended(stream)) { + STREAM_LOG(LogLevel::Verbose, ("MediaStream %p is blocked due to being suspended", stream)); + MarkStreamBlocking(stream); + continue; + } + bool underrun = WillUnderrun(stream, aTime, aEndBlockingDecisions, aEnd); if (underrun) { // We'll block indefinitely MarkStreamBlocking(stream); *aEnd = std::min(*aEnd, aEndBlockingDecisions); continue; } } @@ -1258,19 +1245,19 @@ MediaStreamGraphImpl::ShouldUpdateMainTh void MediaStreamGraphImpl::PrepareUpdatesToMainThreadState(bool aFinalUpdate) { mMonitor.AssertCurrentThreadOwns(); // We don't want to frequently update the main thread about timing update // when we are not running in realtime. if (aFinalUpdate || ShouldUpdateMainThread()) { - mStreamUpdates.SetCapacity(mStreamUpdates.Length() + mStreams.Length()); - for (uint32_t i = 0; i < mStreams.Length(); ++i) { - MediaStream* stream = mStreams[i]; + mStreamUpdates.SetCapacity(mStreamUpdates.Length() + mStreams.Length() + + mSuspendedStreams.Length()); + for (MediaStream* stream : AllStreams()) { if (!stream->MainThreadNeedsUpdates()) { continue; } StreamUpdate* update = mStreamUpdates.AppendElement(); update->mStream = stream; update->mNextMainThreadCurrentTime = GraphTimeToStreamTime(stream, mProcessedTime); update->mNextMainThreadFinished = stream->mNotifiedFinished; @@ -1324,19 +1311,18 @@ MediaStreamGraphImpl::ProduceDataForStre t = next; } NS_ASSERTION(t == aTo, "Something went wrong with rounding to block boundaries"); } bool MediaStreamGraphImpl::AllFinishedStreamsNotified() { - for (uint32_t i = 0; i < mStreams.Length(); ++i) { - MediaStream* s = mStreams[i]; - if (s->mFinished && !s->mNotifiedFinished) { + for (MediaStream* stream : AllStreams()) { + if (stream->mFinished && !stream->mNotifiedFinished) { return false; } } return true; } void MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecision) @@ -1471,18 +1457,18 @@ MediaStreamGraphImpl::Process(GraphTime bool MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd) { { MonitorAutoLock lock(mMemoryReportMonitor); if (mNeedsMemoryReport) { mNeedsMemoryReport = false; - for (uint32_t i = 0; i < mStreams.Length(); ++i) { - AudioNodeStream* stream = mStreams[i]->AsAudioNodeStream(); + for (MediaStream* s : AllStreams()) { + AudioNodeStream* stream = s->AsAudioNodeStream(); if (stream) { AudioNodeSizes usage; stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, usage); mAudioStreamSizes.AppendElement(usage); } } lock.Notify(); @@ -1591,18 +1577,18 @@ public: mGraph->Destroy(); } else { // The graph is not empty. We must be in a forced shutdown, or a // non-realtime graph that has finished processing. Some later // AppendMessage will detect that the manager has been emptied, and // delete it. NS_ASSERTION(mGraph->mForceShutDown || !mGraph->mRealtime, "Not in forced shutdown?"); - for (uint32_t i = 0; i < mGraph->mStreams.Length(); ++i) { - DOMMediaStream* s = mGraph->mStreams[i]->GetWrapper(); + for (MediaStream* stream : mGraph->AllStreams()) { + DOMMediaStream* s = stream->GetWrapper(); if (s) { s->NotifyMediaStreamGraphShutdown(); } } mGraph->mLifecycleState = MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION; } @@ -3187,29 +3173,22 @@ MediaStreamGraph::NotifyWhenGraphStarted graphImpl->AppendMessage(new GraphStartedNotificationControlMessage(aStream)); } } void MediaStreamGraphImpl::ResetVisitedStreamState() { // Reset the visited/consumed/blocked state of the streams. - nsTArray<MediaStream*>* runningAndSuspendedPair[2]; - runningAndSuspendedPair[0] = &mStreams; - runningAndSuspendedPair[1] = &mSuspendedStreams; - - for (uint32_t array = 0; array < 2; array++) { - for (uint32_t i = 0; i < runningAndSuspendedPair[array]->Length(); ++i) { - ProcessedMediaStream* ps = - (*runningAndSuspendedPair[array])[i]->AsProcessedStream(); - if (ps) { - ps->mCycleMarker = NOT_VISITED; - ps->mIsConsumed = false; - ps->mInBlockingSet = false; - } + for (MediaStream* stream : AllStreams()) { + ProcessedMediaStream* ps = stream->AsProcessedStream(); + if (ps) { + ps->mCycleMarker = NOT_VISITED; + ps->mIsConsumed = false; + ps->mInBlockingSet = false; } } } void MediaStreamGraphImpl::StreamSetForAudioContext(dom::AudioContext::AudioContextId aAudioContextId, mozilla::LinkedList<MediaStream>& aStreamSet) { @@ -3245,23 +3224,16 @@ MediaStreamGraphImpl::MoveStreams(AudioC // It is posible to not find the stream here, if there has been two // suspend/resume/close calls in a row. auto i = from.IndexOf(stream); if (i != from.NoIndex) { from.RemoveElementAt(i); to.AppendElement(stream); } - // If streams got added during a period where an AudioContext was suspended, - // set their buffer start time to the appropriate value now: - if (aAudioContextOperation == AudioContextOperation::Resume && - stream->mBufferStartTime == START_TIME_DELAYED) { - stream->mBufferStartTime = mProcessedTime; - } - stream->remove(); } STREAM_LOG(LogLevel::Debug, ("Moving streams between suspended and running" "state: mStreams: %d, mSuspendedStreams: %d\n", mStreams.Length(), mSuspendedStreams.Length())); #ifdef DEBUG // The intersection of the two arrays should be null. for (uint32_t i = 0; i < mStreams.Length(); i++) {
--- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -523,16 +523,66 @@ public: // Capture Stream API. This allows to get a mixed-down output for a window. void RegisterCaptureStreamForWindow(uint64_t aWindowId, ProcessedMediaStream* aCaptureStream); void UnregisterCaptureStreamForWindow(uint64_t aWindowId); already_AddRefed<MediaInputPort> ConnectToCaptureStream(uint64_t aWindowId, MediaStream* aMediaStream); + class StreamSet { + public: + class iterator { + public: + explicit iterator(MediaStreamGraphImpl& aGraph) + : mGraph(&aGraph), mArrayNum(-1), mArrayIndex(0) + { + ++(*this); + } + iterator() : mGraph(nullptr), mArrayNum(2), mArrayIndex(0) {} + MediaStream* operator*() + { + return Array()->ElementAt(mArrayIndex); + } + iterator operator++() + { + ++mArrayIndex; + while (mArrayNum < 2 && + (mArrayNum < 0 || mArrayIndex >= Array()->Length())) { + ++mArrayNum; + mArrayIndex = 0; + } + return *this; + } + bool operator==(const iterator& aOther) const + { + return mArrayNum == aOther.mArrayNum && mArrayIndex == aOther.mArrayIndex; + } + bool operator!=(const iterator& aOther) const + { + return !(*this == aOther); + } + private: + nsTArray<MediaStream*>* Array() + { + return mArrayNum == 0 ? &mGraph->mStreams : &mGraph->mSuspendedStreams; + } + MediaStreamGraphImpl* mGraph; + int mArrayNum; + uint32_t mArrayIndex; + }; + + explicit StreamSet(MediaStreamGraphImpl& aGraph) : mGraph(aGraph) {} + iterator begin() { return iterator(mGraph); } + iterator end() { return iterator(); } + private: + MediaStreamGraphImpl& mGraph; + }; + StreamSet AllStreams() { return StreamSet(*this); } + // Data members // /** * Graphs own owning references to their driver, until shutdown. When a driver * switch occur, previous driver is either deleted, or it's ownership is * passed to a event that will take care of the asynchronous cleanup, as * audio stream can take some time to shut down. */
--- a/dom/media/XiphExtradata.cpp +++ b/dom/media/XiphExtradata.cpp @@ -64,17 +64,17 @@ bool XiphExtradataToHeaders(nsTArray<uns // to underflow. if (aAvailable - total < headerLen) { return false; } aHeaderLens.AppendElement(headerLen); // Since we know aAvailable >= total + headerLen, this can't overflow. total += headerLen; } - aHeaderLens.AppendElement(aAvailable); + aHeaderLens.AppendElement(aAvailable - total); for (int i = 0; i < nHeaders; i++) { aHeaders.AppendElement(aData); aData += aHeaderLens[i]; } return true; } } // namespace mozilla
--- a/dom/media/mediasink/AudioSinkWrapper.cpp +++ b/dom/media/mediasink/AudioSinkWrapper.cpp @@ -29,19 +29,19 @@ AudioSinkWrapper::GetPlaybackParams() co return mParams; } void AudioSinkWrapper::SetPlaybackParams(const PlaybackParams& aParams) { AssertOwnerThread(); if (mAudioSink) { - mAudioSink->SetVolume(aParams.volume); - mAudioSink->SetPlaybackRate(aParams.playbackRate); - mAudioSink->SetPreservesPitch(aParams.preservesPitch); + mAudioSink->SetVolume(aParams.mVolume); + mAudioSink->SetPlaybackRate(aParams.mPlaybackRate); + mAudioSink->SetPreservesPitch(aParams.mPreservesPitch); } mParams = aParams; } nsRefPtr<GenericPromise> AudioSinkWrapper::OnEnded(TrackType aType) { AssertOwnerThread(); @@ -59,55 +59,94 @@ AudioSinkWrapper::GetEndTime(TrackType a MOZ_ASSERT(mIsStarted, "Must be called after playback starts."); if (aType == TrackInfo::kAudioTrack && mAudioSink) { return mAudioSink->GetEndTime(); } return -1; } int64_t -AudioSinkWrapper::GetPosition() const +AudioSinkWrapper::GetVideoPosition(TimeStamp aNow) const +{ + AssertOwnerThread(); + MOZ_ASSERT(!mPlayStartTime.IsNull()); + // Time elapsed since we started playing. + int64_t delta = (aNow - mPlayStartTime).ToMicroseconds(); + // Take playback rate into account. + return mPlayDuration + delta * mParams.mPlaybackRate; +} + +int64_t +AudioSinkWrapper::GetPosition(TimeStamp* aTimeStamp) const { AssertOwnerThread(); MOZ_ASSERT(mIsStarted, "Must be called after playback starts."); - return mAudioSink->GetPosition(); + + int64_t pos = -1; + TimeStamp t = TimeStamp::Now(); + + if (!mAudioEnded) { + // Rely on the audio sink to report playback position when it is not ended. + pos = mAudioSink->GetPosition(); + } else if (!mPlayStartTime.IsNull()) { + // Calculate playback position using system clock if we are still playing. + pos = GetVideoPosition(t); + } else { + // Return how long we've played if we are not playing. + pos = mPlayDuration; + } + + if (aTimeStamp) { + *aTimeStamp = t; + } + + return pos; } bool AudioSinkWrapper::HasUnplayedFrames(TrackType aType) const { AssertOwnerThread(); return mAudioSink ? mAudioSink->HasUnplayedFrames() : false; } void AudioSinkWrapper::SetVolume(double aVolume) { AssertOwnerThread(); - mParams.volume = aVolume; + mParams.mVolume = aVolume; if (mAudioSink) { mAudioSink->SetVolume(aVolume); } } void AudioSinkWrapper::SetPlaybackRate(double aPlaybackRate) { AssertOwnerThread(); - mParams.playbackRate = aPlaybackRate; - if (mAudioSink) { + mParams.mPlaybackRate = aPlaybackRate; + if (!mAudioEnded) { + // Pass the playback rate to the audio sink. The underlying AudioStream + // will handle playback rate changes and report correct audio position. mAudioSink->SetPlaybackRate(aPlaybackRate); + } else if (!mPlayStartTime.IsNull()) { + // Adjust playback duration and start time when we are still playing. + TimeStamp now = TimeStamp::Now(); + mPlayDuration = GetVideoPosition(now); + mPlayStartTime = now; } + // Do nothing when not playing. Changes in playback rate will be taken into + // account by GetVideoPosition(). } void AudioSinkWrapper::SetPreservesPitch(bool aPreservesPitch) { AssertOwnerThread(); - mParams.preservesPitch = aPreservesPitch; + mParams.mPreservesPitch = aPreservesPitch; if (mAudioSink) { mAudioSink->SetPreservesPitch(aPreservesPitch); } } void AudioSinkWrapper::SetPlaying(bool aPlaying) { @@ -116,45 +155,85 @@ AudioSinkWrapper::SetPlaying(bool aPlayi // Resume/pause matters only when playback started. if (!mIsStarted) { return; } if (mAudioSink) { mAudioSink->SetPlaying(aPlaying); } + + if (aPlaying) { + MOZ_ASSERT(mPlayStartTime.IsNull()); + mPlayStartTime = TimeStamp::Now(); + } else { + // Remember how long we've played. + mPlayDuration = GetPosition(); + // mPlayStartTime must be updated later since GetPosition() + // depends on the value of mPlayStartTime. + mPlayStartTime = TimeStamp(); + } } void AudioSinkWrapper::Start(int64_t aStartTime, const MediaInfo& aInfo) { AssertOwnerThread(); MOZ_ASSERT(!mIsStarted, "playback already started."); mIsStarted = true; + mPlayDuration = aStartTime; + mPlayStartTime = TimeStamp::Now(); - mAudioSink = mCreator->Create(); - mEndPromise = mAudioSink->Init(); - SetPlaybackParams(mParams); + // no audio is equivalent to audio ended before video starts. + mAudioEnded = !aInfo.HasAudio(); + + if (aInfo.HasAudio()) { + mAudioSink = mCreator->Create(); + mEndPromise = mAudioSink->Init(); + SetPlaybackParams(mParams); + + mAudioSinkPromise.Begin(mEndPromise->Then( + mOwnerThread.get(), __func__, this, + &AudioSinkWrapper::OnAudioEnded, + &AudioSinkWrapper::OnAudioEnded)); + } } void AudioSinkWrapper::Stop() { AssertOwnerThread(); MOZ_ASSERT(mIsStarted, "playback not started."); mIsStarted = false; - mAudioSink->Shutdown(); - mAudioSink = nullptr; - mEndPromise = nullptr; + mAudioEnded = true; + + if (mAudioSink) { + mAudioSinkPromise.DisconnectIfExists(); + mAudioSink->Shutdown(); + mAudioSink = nullptr; + mEndPromise = nullptr; + } } bool AudioSinkWrapper::IsStarted() const { AssertOwnerThread(); return mIsStarted; } +void +AudioSinkWrapper::OnAudioEnded() +{ + AssertOwnerThread(); + mAudioSinkPromise.Complete(); + mPlayDuration = GetPosition(); + if (!mPlayStartTime.IsNull()) { + mPlayStartTime = TimeStamp::Now(); + } + mAudioEnded = true; +} + } // namespace media } // namespace mozilla
--- a/dom/media/mediasink/AudioSinkWrapper.h +++ b/dom/media/mediasink/AudioSinkWrapper.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef AudioSinkWrapper_h_ #define AudioSinkWrapper_h_ #include "mozilla/AbstractThread.h" #include "mozilla/dom/AudioChannelBinding.h" #include "mozilla/nsRefPtr.h" +#include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "MediaSink.h" namespace mozilla { class MediaData; template <class T> class MediaQueue; @@ -45,24 +46,27 @@ class AudioSinkWrapper : public MediaSin }; public: template <typename Function> AudioSinkWrapper(AbstractThread* aOwnerThread, const Function& aFunc) : mOwnerThread(aOwnerThread) , mCreator(new CreatorImpl<Function>(aFunc)) , mIsStarted(false) + // Give an insane value to facilitate debug if used before playback starts. + , mPlayDuration(INT64_MAX) + , mAudioEnded(true) {} const PlaybackParams& GetPlaybackParams() const override; void SetPlaybackParams(const PlaybackParams& aParams) override; nsRefPtr<GenericPromise> OnEnded(TrackType aType) override; int64_t GetEndTime(TrackType aType) const override; - int64_t GetPosition() const override; + int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override; bool HasUnplayedFrames(TrackType aType) const override; void SetVolume(double aVolume) override; void SetPlaybackRate(double aPlaybackRate) override; void SetPreservesPitch(bool aPreservesPitch) override; void SetPlaying(bool aPlaying) override; void Start(int64_t aStartTime, const MediaInfo& aInfo) override; @@ -73,21 +77,31 @@ public: private: virtual ~AudioSinkWrapper(); void AssertOwnerThread() const { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); } + int64_t GetVideoPosition(TimeStamp aNow) const; + + void OnAudioEnded(); + const nsRefPtr<AbstractThread> mOwnerThread; UniquePtr<Creator> mCreator; nsRefPtr<AudioSink> mAudioSink; nsRefPtr<GenericPromise> mEndPromise; bool mIsStarted; PlaybackParams mParams; + + TimeStamp mPlayStartTime; + int64_t mPlayDuration; + + bool mAudioEnded; + MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise; }; } // namespace media } // namespace mozilla #endif //AudioSinkWrapper_h_
rename from dom/media/DecodedStream.cpp rename to dom/media/mediasink/DecodedStream.cpp --- a/dom/media/DecodedStream.cpp +++ b/dom/media/mediasink/DecodedStream.cpp @@ -1,14 +1,17 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "mozilla/CheckedInt.h" +#include "mozilla/gfx/Point.h" + #include "AudioSegment.h" #include "DecodedStream.h" #include "MediaData.h" #include "MediaQueue.h" #include "MediaStreamGraph.h" #include "SharedBuffer.h" #include "VideoSegment.h" #include "VideoUtils.h" @@ -245,20 +248,17 @@ OutputStreamData::Init(OutputStreamManag void OutputStreamData::Connect(MediaStream* aStream) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mPort, "Already connected?"); MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream."); - // The output stream must stay in sync with the input stream, so if - // either stream is blocked, we block the other. - mPort = mStream->AllocateInputPort(aStream, - MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT); + mPort = mStream->AllocateInputPort(aStream, 0); // Unblock the output stream now. The input stream is responsible for // controlling blocking from now on. mStream->ChangeExplicitBlockerCount(-1); } bool OutputStreamData::Disconnect() { @@ -353,43 +353,73 @@ OutputStreamManager::Disconnect() } DecodedStream::DecodedStream(AbstractThread* aOwnerThread, MediaQueue<MediaData>& aAudioQueue, MediaQueue<MediaData>& aVideoQueue) : mOwnerThread(aOwnerThread) , mShuttingDown(false) , mPlaying(false) - , mVolume(1.0) , mSameOrigin(false) , mAudioQueue(aAudioQueue) , mVideoQueue(aVideoQueue) { } DecodedStream::~DecodedStream() { MOZ_ASSERT(mStartTime.isNothing(), "playback should've ended."); } +const media::MediaSink::PlaybackParams& +DecodedStream::GetPlaybackParams() const +{ + AssertOwnerThread(); + return mParams; +} + void -DecodedStream::Shutdown() +DecodedStream::SetPlaybackParams(const PlaybackParams& aParams) +{ + AssertOwnerThread(); + mParams = aParams; +} + +nsRefPtr<GenericPromise> +DecodedStream::OnEnded(TrackType aType) +{ + AssertOwnerThread(); + MOZ_ASSERT(mStartTime.isSome()); + + if (aType == TrackInfo::kAudioTrack) { + // TODO: we should return a promise which is resolved when the audio track + // is finished. For now this promise is resolved when the whole stream is + // finished. + return mFinishPromise; + } + // TODO: handle video track. + return nullptr; +} + +void +DecodedStream::BeginShutdown() { MOZ_ASSERT(NS_IsMainThread()); mShuttingDown = true; } -nsRefPtr<GenericPromise> -DecodedStream::StartPlayback(int64_t aStartTime, const MediaInfo& aInfo) +void +DecodedStream::Start(int64_t aStartTime, const MediaInfo& aInfo) { AssertOwnerThread(); MOZ_ASSERT(mStartTime.isNothing(), "playback already started."); mStartTime.emplace(aStartTime); mInfo = aInfo; + mPlaying = true; ConnectListener(); class R : public nsRunnable { typedef MozPromiseHolder<GenericPromise> Promise; typedef void(DecodedStream::*Method)(Promise&&); public: R(DecodedStream* aThis, Method aMethod, Promise&& aPromise) : mThis(aThis), mMethod(aMethod) @@ -403,40 +433,43 @@ DecodedStream::StartPlayback(int64_t aSt } private: nsRefPtr<DecodedStream> mThis; Method mMethod; Promise mPromise; }; MozPromiseHolder<GenericPromise> promise; - nsRefPtr<GenericPromise> rv = promise.Ensure(__func__); + mFinishPromise = promise.Ensure(__func__); nsCOMPtr<nsIRunnable> r = new R(this, &DecodedStream::CreateData, Move(promise)); AbstractThread::MainThread()->Dispatch(r.forget()); - - return rv.forget(); } -void DecodedStream::StopPlayback() +void +DecodedStream::Stop() { AssertOwnerThread(); - - // Playback didn't even start at all. - if (mStartTime.isNothing()) { - return; - } + MOZ_ASSERT(mStartTime.isSome(), "playback not started."); mStartTime.reset(); DisconnectListener(); + mFinishPromise = nullptr; // Clear mData immediately when this playback session ends so we won't // send data to the wrong stream in SendData() in next playback session. DestroyData(Move(mData)); } +bool +DecodedStream::IsStarted() const +{ + AssertOwnerThread(); + return mStartTime.isSome(); +} + void DecodedStream::DestroyData(UniquePtr<DecodedStreamData> aData) { AssertOwnerThread(); if (!aData) { return; } @@ -526,27 +559,47 @@ DecodedStream::RemoveOutput(MediaStream* { mOutputStreamManager.Remove(aStream); } void DecodedStream::SetPlaying(bool aPlaying) { AssertOwnerThread(); + + // Resume/pause matters only when playback started. + if (mStartTime.isNothing()) { + return; + } + mPlaying = aPlaying; if (mData) { mData->SetPlaying(aPlaying); } } void DecodedStream::SetVolume(double aVolume) { AssertOwnerThread(); - mVolume = aVolume; + mParams.mVolume = aVolume; +} + +void +DecodedStream::SetPlaybackRate(double aPlaybackRate) +{ + AssertOwnerThread(); + mParams.mPlaybackRate = aPlaybackRate; +} + +void +DecodedStream::SetPreservesPitch(bool aPreservesPitch) +{ + AssertOwnerThread(); + mParams.mPreservesPitch = aPreservesPitch; } void DecodedStream::SetSameOrigin(bool aSameOrigin) { AssertOwnerThread(); mSameOrigin = aSameOrigin; } @@ -810,50 +863,55 @@ DecodedStream::SendData() } // Nothing to do when the stream is finished. if (mData->mHaveSentFinish) { return; } InitTracks(); - SendAudio(mVolume, mSameOrigin); + SendAudio(mParams.mVolume, mSameOrigin); SendVideo(mSameOrigin); AdvanceTracks(); bool finished = (!mInfo.HasAudio() || mAudioQueue.IsFinished()) && (!mInfo.HasVideo() || mVideoQueue.IsFinished()); if (finished && !mData->mHaveSentFinish) { mData->mHaveSentFinish = true; mData->mStream->Finish(); } } int64_t -DecodedStream::AudioEndTime() const +DecodedStream::GetEndTime(TrackType aType) const { AssertOwnerThread(); - if (mStartTime.isSome() && mInfo.HasAudio() && mData) { + if (aType == TrackInfo::kAudioTrack && mInfo.HasAudio() && mData) { CheckedInt64 t = mStartTime.ref() + FramesToUsecs(mData->mAudioFramesWritten, mInfo.mAudio.mRate); if (t.isValid()) { return t.value(); } + } else if (aType == TrackInfo::kVideoTrack && mData) { + return mData->mNextVideoTime; } return -1; } int64_t -DecodedStream::GetPosition() const +DecodedStream::GetPosition(TimeStamp* aTimeStamp) const { AssertOwnerThread(); // This is only called after MDSM starts playback. So mStartTime is // guaranteed to be something. MOZ_ASSERT(mStartTime.isSome()); + if (aTimeStamp) { + *aTimeStamp = TimeStamp::Now(); + } return mStartTime.ref() + (mData ? mData->GetPosition() : 0); } bool DecodedStream::IsFinished() const { AssertOwnerThread(); return mData && mData->IsFinished();
rename from dom/media/DecodedStream.h rename to dom/media/mediasink/DecodedStream.h --- a/dom/media/DecodedStream.h +++ b/dom/media/mediasink/DecodedStream.h @@ -5,38 +5,36 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef DecodedStream_h_ #define DecodedStream_h_ #include "nsTArray.h" #include "MediaEventSource.h" #include "MediaInfo.h" +#include "MediaSink.h" #include "mozilla/AbstractThread.h" -#include "mozilla/CheckedInt.h" #include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" #include "mozilla/nsRefPtr.h" -#include "mozilla/ReentrantMonitor.h" #include "mozilla/UniquePtr.h" -#include "mozilla/gfx/Point.h" namespace mozilla { class DecodedStream; class DecodedStreamData; class MediaData; class MediaInputPort; class MediaStream; class MediaStreamGraph; class OutputStreamListener; class OutputStreamManager; class ProcessedMediaStream; -class ReentrantMonitor; +class TimeStamp; template <class T> class MediaQueue; namespace layers { class Image; } // namespace layers class OutputStreamData { @@ -94,44 +92,51 @@ public: private: // Keep the input stream so we can connect the output streams that // are added after Connect(). nsRefPtr<MediaStream> mInputStream; nsTArray<OutputStreamData> mStreams; }; -class DecodedStream { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream); +class DecodedStream : public media::MediaSink { + using media::MediaSink::PlaybackParams; + public: DecodedStream(AbstractThread* aOwnerThread, MediaQueue<MediaData>& aAudioQueue, MediaQueue<MediaData>& aVideoQueue); - void Shutdown(); + // MediaSink functions. + const PlaybackParams& GetPlaybackParams() const override; + void SetPlaybackParams(const PlaybackParams& aParams) override; - // Mimic MDSM::StartAudioThread. - // Must be called before any calls to SendData(). - // - // Return a promise which will be resolved when the stream is finished - // or rejected if any error. - nsRefPtr<GenericPromise> StartPlayback(int64_t aStartTime, - const MediaInfo& aInfo); - // Mimic MDSM::StopAudioThread. - void StopPlayback(); + nsRefPtr<GenericPromise> OnEnded(TrackType aType) override; + int64_t GetEndTime(TrackType aType) const override; + int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const override; + bool HasUnplayedFrames(TrackType aType) const override + { + // TODO: implement this. + return false; + } + void SetVolume(double aVolume) override; + void SetPlaybackRate(double aPlaybackRate) override; + void SetPreservesPitch(bool aPreservesPitch) override; + void SetPlaying(bool aPlaying) override; + + void Start(int64_t aStartTime, const MediaInfo& aInfo) override; + void Stop() override; + bool IsStarted() const override; + + // TODO: fix these functions that don't fit into the interface of MediaSink. + void BeginShutdown(); void AddOutput(ProcessedMediaStream* aStream, bool aFinishWhenEnded); void RemoveOutput(MediaStream* aStream); - - void SetPlaying(bool aPlaying); - void SetVolume(double aVolume); void SetSameOrigin(bool aSameOrigin); - - int64_t AudioEndTime() const; - int64_t GetPosition() const; bool IsFinished() const; bool HasConsumers() const; protected: virtual ~DecodedStream(); private: void CreateData(MozPromiseHolder<GenericPromise>&& aPromise); @@ -159,20 +164,21 @@ private: OutputStreamManager mOutputStreamManager; // True if MDSM has begun shutdown. bool mShuttingDown; /* * Worker thread only members. */ UniquePtr<DecodedStreamData> mData; + nsRefPtr<GenericPromise> mFinishPromise; bool mPlaying; - double mVolume; bool mSameOrigin; + PlaybackParams mParams; Maybe<int64_t> mStartTime; MediaInfo mInfo; MediaQueue<MediaData>& mAudioQueue; MediaQueue<MediaData>& mVideoQueue; MediaEventListener mAudioPushListener;
--- a/dom/media/mediasink/MediaSink.h +++ b/dom/media/mediasink/MediaSink.h @@ -8,16 +8,19 @@ #define MediaSink_h_ #include "mozilla/nsRefPtr.h" #include "mozilla/MozPromise.h" #include "nsISupportsImpl.h" #include "MediaInfo.h" namespace mozilla { + +class TimeStamp; + namespace media { /** * A consumer of audio/video data which plays audio and video tracks and * manages A/V sync between them. * * A typical sink sends audio/video outputs to the speaker and screen. * However, there are also sinks which capture the output of an media element @@ -32,20 +35,20 @@ namespace media { */ class MediaSink { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSink); typedef mozilla::TrackInfo::TrackType TrackType; struct PlaybackParams { PlaybackParams() - : volume(1.0) , playbackRate(1.0) , preservesPitch(true) {} - double volume; - double playbackRate; - bool preservesPitch; + : mVolume(1.0) , mPlaybackRate(1.0) , mPreservesPitch(true) {} + double mVolume; + double mPlaybackRate; + bool mPreservesPitch; }; // Return the playback parameters of this sink. // Can be called in any state. virtual const PlaybackParams& GetPlaybackParams() const = 0; // Set the playback parameters of this sink. // Can be called in any state. @@ -59,18 +62,20 @@ public: // Return the end time of the audio/video data that has been consumed // or -1 if no such track. // Must be called after playback starts. virtual int64_t GetEndTime(TrackType aType) const = 0; // Return playback position of the media. // Since A/V sync is always maintained by this sink, there is no need to // specify whether we want to get audio or video position. + // aTimeStamp returns the timeStamp corresponding to the returned position + // which is used by the compositor to derive the render time of video frames. // Must be called after playback starts. - virtual int64_t GetPosition() const = 0; + virtual int64_t GetPosition(TimeStamp* aTimeStamp = nullptr) const = 0; // Return true if there are data consumed but not played yet. // Can be called in any state. virtual bool HasUnplayedFrames(TrackType aType) const = 0; // Set volume of the audio track. // Do nothing if this sink has no audio track. // Can be called in any state.
--- a/dom/media/mediasink/moz.build +++ b/dom/media/mediasink/moz.build @@ -2,11 +2,12 @@ # vim: set filetype=python: # 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/. UNIFIED_SOURCES += [ 'AudioSinkWrapper.cpp', 'DecodedAudioDataSink.cpp', + 'DecodedStream.cpp', ] FINAL_LIBRARY = 'xul'
--- a/dom/media/mediasource/SourceBufferDecoder.cpp +++ b/dom/media/mediasource/SourceBufferDecoder.cpp @@ -90,30 +90,16 @@ SourceBufferDecoder::MetadataLoaded(nsAu void SourceBufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) { MSE_DEBUG("UNIMPLEMENTED"); } -void -SourceBufferDecoder::QueueMetadata(const media::TimeUnit& aTime, - nsAutoPtr<MediaInfo> aInfo, - nsAutoPtr<MetadataTags> aTags) -{ - MSE_DEBUG("UNIMPLEMENTED"); -} - -void -SourceBufferDecoder::RemoveMediaTracks() -{ - MSE_DEBUG("UNIMPLEMENTED"); -} - bool SourceBufferDecoder::HasInitializationData() { return true; } void SourceBufferDecoder::OnReadMetadataCompleted()
--- a/dom/media/mediasource/SourceBufferDecoder.h +++ b/dom/media/mediasource/SourceBufferDecoder.h @@ -46,18 +46,16 @@ public: MediaDecoderEventVisibility aEventVisibility) final override; virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) final override; virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override; virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) final override; virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, uint32_t aDropped) final override; virtual void NotifyWaitingForResourcesStatusChanged() final override; virtual void OnReadMetadataCompleted() final override; - virtual void QueueMetadata(const media::TimeUnit& aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) final override; - virtual void RemoveMediaTracks() final override; virtual void SetMediaSeekable(bool aMediaSeekable) final override; virtual bool HasInitializationData() final override; // SourceBufferResource specific interface below. int64_t GetTimestampOffset() const { return mTimestampOffset; } void SetTimestampOffset(int64_t aOffset) { mTimestampOffset = aOffset; } // Warning: this mirrors GetBuffered in MediaDecoder, but this class's base is
--- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -102,17 +102,16 @@ EXPORTS += [ 'AudioCompactor.h', 'AudioMixer.h', 'AudioPacketizer.h', 'AudioSampleFormat.h', 'AudioSegment.h', 'AudioStream.h', 'BufferMediaResource.h', 'CubebUtils.h', - 'DecodedStream.h', 'DecoderTraits.h', 'DOMMediaStream.h', 'EncodedBufferCache.h', 'FileBlockCache.h', 'FlushableTaskQueue.h', 'Intervals.h', 'Latency.h', 'MediaCache.h', @@ -197,17 +196,16 @@ UNIFIED_SOURCES += [ 'AudioCompactor.cpp', 'AudioSegment.cpp', 'AudioStream.cpp', 'AudioStreamTrack.cpp', 'AudioTrack.cpp', 'AudioTrackList.cpp', 'CanvasCaptureMediaStream.cpp', 'CubebUtils.cpp', - 'DecodedStream.cpp', 'DOMMediaStream.cpp', 'EncodedBufferCache.cpp', 'FileBlockCache.cpp', 'FlushableTaskQueue.cpp', 'GetUserMediaRequest.cpp', 'GraphDriver.cpp', 'Latency.cpp', 'MediaCache.cpp',
--- a/dom/media/ogg/OggReader.cpp +++ b/dom/media/ogg/OggReader.cpp @@ -809,20 +809,21 @@ bool OggReader::ReadOggChain() chained = true; tags = newOpusState->GetTags(); } if (chained) { SetChained(true); { - nsAutoPtr<MediaInfo> info(new MediaInfo(mInfo)); - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); auto t = mDecodedAudioFrames * USECS_PER_S / mInfo.mAudio.mRate; - mDecoder->QueueMetadata(media::TimeUnit::FromMicroseconds(t), info, tags); + mTimedMetadataEvent.Notify( + TimedMetadata(media::TimeUnit::FromMicroseconds(t), + Move(tags), + nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)))); } return true; } return false; } nsresult OggReader::DecodeTheora(ogg_packet* aPacket, int64_t aTimeThreshold)
--- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -852,21 +852,16 @@ AudioContext::Suspend(ErrorResult& aRv) if (mAudioContextState == AudioContextState::Suspended) { promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); } Destination()->Suspend(); - MediaStream* ds = DestinationStream(); - if (ds) { - ds->BlockStreamIfNeeded(); - } - mPromiseGripArray.AppendElement(promise); Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(), AudioContextOperation::Suspend, promise); return promise.forget(); } already_AddRefed<Promise> @@ -892,21 +887,16 @@ AudioContext::Resume(ErrorResult& aRv) if (mAudioContextState == AudioContextState::Running) { promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); } Destination()->Resume(); - MediaStream* ds = DestinationStream(); - if (ds) { - ds->UnblockStreamIfNeeded(); - } - mPromiseGripArray.AppendElement(promise); Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(), AudioContextOperation::Resume, promise); return promise.forget(); } already_AddRefed<Promise> @@ -938,20 +928,16 @@ AudioContext::Close(ErrorResult& aRv) mPromiseGripArray.AppendElement(promise); // This can be called when freeing a document, and the streams are dead at // this point, so we need extra null-checks. MediaStream* ds = DestinationStream(); if (ds) { Graph()->ApplyAudioContextOperation(ds->AsAudioNodeStream(), AudioContextOperation::Close, promise); - - if (ds) { - ds->BlockStreamIfNeeded(); - } } return promise.forget(); } void AudioContext::UpdateNodeCount(int32_t aDelta) { bool firstNode = mNodeCount == 0;
--- a/dom/media/webaudio/AudioNode.cpp +++ b/dom/media/webaudio/AudioNode.cpp @@ -220,17 +220,17 @@ AudioNode::Connect(AudioNode& aDestinati input->mInputPort = aInput; input->mOutputPort = aOutput; AudioNodeStream* destinationStream = aDestination.mStream; if (mStream && destinationStream) { // Connect streams in the MediaStreamGraph MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number"); MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); input->mStreamPort = destinationStream-> - AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT, + AllocateInputPort(mStream, 0, static_cast<uint16_t>(aInput), static_cast<uint16_t>(aOutput)); } aDestination.NotifyInputsChanged(); // This connection may have connected a panner and a source. Context()->UpdatePannerSource(); } @@ -263,18 +263,17 @@ AudioNode::Connect(AudioParam& aDestinat MediaStream* stream = aDestination.Stream(); MOZ_ASSERT(stream->AsProcessedStream()); ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream); if (mStream) { // Setup our stream as an input to the AudioParam's stream MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number"); input->mStreamPort = - ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT, - 0, static_cast<uint16_t>(aOutput)); + ps->AllocateInputPort(mStream, 0, 0, static_cast<uint16_t>(aOutput)); } } void AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue) { MOZ_ASSERT(mStream, "How come we don't have a stream here?"); mStream->SetDoubleParameter(aIndex, aValue);
--- a/dom/media/webaudio/AudioParam.cpp +++ b/dom/media/webaudio/AudioParam.cpp @@ -109,18 +109,17 @@ AudioParam::Stream() // Mark as an AudioParam helper stream stream->SetAudioParamHelperStream(); mStream = stream.forget(); // Setup the AudioParam's stream as an input to the owner AudioNode's stream AudioNodeStream* nodeStream = mNode->GetStream(); if (nodeStream) { - mNodeStreamPort = - nodeStream->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT); + mNodeStreamPort = nodeStream->AllocateInputPort(mStream, 0); } // Let the MSG's copy of AudioParamTimeline know about the change in the stream mCallback(mNode); return mStream; }
--- a/dom/media/webaudio/BufferDecoder.cpp +++ b/dom/media/webaudio/BufferDecoder.cpp @@ -125,28 +125,16 @@ BufferDecoder::MetadataLoaded(nsAutoPtr< void BufferDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) { // ignore } void -BufferDecoder::QueueMetadata(const media::TimeUnit& aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) -{ - // ignore -} - -void -BufferDecoder::RemoveMediaTracks() -{ - // ignore -} - -void BufferDecoder::OnReadMetadataCompleted() { // ignore } void BufferDecoder::NotifyWaitingForResourcesStatusChanged() {
--- a/dom/media/webaudio/BufferDecoder.h +++ b/dom/media/webaudio/BufferDecoder.h @@ -53,22 +53,19 @@ public: virtual bool IsTransportSeekable() final override; virtual bool IsMediaSeekable() final override; virtual void MetadataLoaded(nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags, MediaDecoderEventVisibility aEventVisibility) final override; - virtual void QueueMetadata(const media::TimeUnit& aTime, nsAutoPtr<MediaInfo> aInfo, nsAutoPtr<MetadataTags> aTags) final override; virtual void FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) final override; - virtual void RemoveMediaTracks() final override; - virtual void OnReadMetadataCompleted() final override; virtual MediaDecoderOwner* GetOwner() final override; virtual void NotifyWaitingForResourcesStatusChanged() final override; virtual void NotifyDataArrived(uint32_t, int64_t, bool) final override {};
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.cpp +++ b/dom/media/webaudio/MediaStreamAudioSourceNode.cpp @@ -36,18 +36,17 @@ MediaStreamAudioSourceNode::MediaStreamA 2, ChannelCountMode::Max, ChannelInterpretation::Speakers), mInputStream(aMediaStream) { AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this); mStream = AudioNodeExternalInputStream::Create(aContext->Graph(), engine); ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get()); - mInputPort = outputStream->AllocateInputPort(aMediaStream->GetStream(), - MediaInputPort::FLAG_BLOCK_INPUT); + mInputPort = outputStream->AllocateInputPort(aMediaStream->GetStream(), 0); mInputStream->AddConsumerToKeepAlive(static_cast<nsIDOMEventTarget*>(this)); PrincipalChanged(mInputStream); // trigger enabling/disabling of the connector mInputStream->AddPrincipalChangeObserver(this); } MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() {
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp @@ -157,16 +157,59 @@ MediaEngineCameraVideoSource::LogConstra c.mHeight.mIdeal.WasPassed()? c.mHeight.mIdeal.Value() : 0)); LOG(((c.mFrameRate.mIdeal.WasPassed()? " frameRate: { min: %f, max: %f, ideal: %f }" : " frameRate: { min: %f, max: %f }"), c.mFrameRate.mMin, c.mFrameRate.mMax, c.mFrameRate.mIdeal.WasPassed()? c.mFrameRate.mIdeal.Value() : 0)); } +void +MediaEngineCameraVideoSource::LogCapability(const char* aHeader, + const webrtc::CaptureCapability &aCapability, uint32_t aDistance) +{ + // RawVideoType and VideoCodecType media/webrtc/trunk/webrtc/common_types.h + static const char* const types[] = { + "I420", + "YV12", + "YUY2", + "UYVY", + "IYUV", + "ARGB", + "RGB24", + "RGB565", + "ARGB4444", + "ARGB1555", + "MJPEG", + "NV12", + "NV21", + "BGRA", + "Unknown type" + }; + + static const char* const codec[] = { + "VP8", + "VP9", + "H264", + "I420", + "RED", + "ULPFEC", + "Generic codec", + "Unknown codec" + }; + + LOG(("%s: %4u x %4u x %2u maxFps, %s, %s. Distance = %lu", + aHeader, aCapability.width, aCapability.height, aCapability.maxFPS, + types[std::min(std::max(uint32_t(0), uint32_t(aCapability.rawType)), + uint32_t(sizeof(types) / sizeof(*types) - 1))], + codec[std::min(std::max(uint32_t(0), uint32_t(aCapability.codecType)), + uint32_t(sizeof(codec) / sizeof(*codec) - 1))], + aDistance)); +} + bool MediaEngineCameraVideoSource::ChooseCapability( const MediaTrackConstraints &aConstraints, const MediaEnginePrefs &aPrefs, const nsString& aDeviceId) { if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) { LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps", @@ -190,16 +233,17 @@ MediaEngineCameraVideoSource::ChooseCapa // First, filter capabilities by required constraints (min, max, exact). for (size_t i = 0; i < candidateSet.Length();) { auto& candidate = candidateSet[i]; webrtc::CaptureCapability cap; GetCapability(candidate.mIndex, cap); candidate.mDistance = GetFitnessDistance(cap, aConstraints, false, aDeviceId); + LogCapability("Capability", cap, candidate.mDistance); if (candidate.mDistance == UINT32_MAX) { candidateSet.RemoveElementAt(i); } else { ++i; } } // Filter further with all advanced constraints (that don't overconstrain). @@ -229,16 +273,17 @@ MediaEngineCameraVideoSource::ChooseCapa } // Remaining algorithm is up to the UA. TrimLessFitCandidates(candidateSet); // Any remaining multiples all have the same distance. A common case of this // occurs when no ideal is specified. Lean toward defaults. + uint32_t sameDistance = candidateSet[0].mDistance; { MediaTrackConstraintSet prefs; prefs.mWidth.SetAsLong() = aPrefs.GetWidth(); prefs.mHeight.SetAsLong() = aPrefs.GetHeight(); prefs.mFrameRate.SetAsDouble() = aPrefs.mFPS; for (auto& candidate : candidateSet) { webrtc::CaptureCapability cap; @@ -263,19 +308,17 @@ MediaEngineCameraVideoSource::ChooseCapa found = true; break; } } if (!found) { GetCapability(candidateSet[0].mIndex, mCapability); } - LOG(("chose cap %dx%d @%dfps codec %d raw %d", - mCapability.width, mCapability.height, mCapability.maxFPS, - mCapability.codecType, mCapability.rawType)); + LogCapability("Chosen capability", mCapability, sameDistance); return true; } void MediaEngineCameraVideoSource::SetName(nsString aName) { mDeviceName = aName; bool hasFacingMode = false;
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h +++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h @@ -79,16 +79,19 @@ protected: StreamTime delta); uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate, const dom::MediaTrackConstraintSet &aConstraints, bool aAdvanced, const nsString& aDeviceId); static void TrimLessFitCandidates(CapabilitySet& set); static void LogConstraints(const dom::MediaTrackConstraintSet& aConstraints, bool aAdvanced); +static void LogCapability(const char* aHeader, + const webrtc::CaptureCapability &aCapability, + uint32_t aDistance); virtual size_t NumCapabilities(); virtual void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut); bool ChooseCapability(const dom::MediaTrackConstraints &aConstraints, const MediaEnginePrefs &aPrefs, const nsString& aDeviceId); void SetName(nsString aName); void SetUUID(const char* aUUID); const nsCString& GetUUID(); // protected access
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp +++ b/dom/media/webspeech/synth/nsSpeechTask.cpp @@ -135,25 +135,29 @@ nsSpeechTask::~nsSpeechTask() if (mPort) { mPort->Destroy(); mPort = nullptr; } } void -nsSpeechTask::Init(ProcessedMediaStream* aStream) +nsSpeechTask::InitDirectAudio() { - if (aStream) { - mStream = aStream->Graph()->CreateSourceStream(nullptr); - mPort = aStream->AllocateInputPort(mStream, 0); - mIndirectAudio = false; - } else { - mIndirectAudio = true; - } + mStream = MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, + AudioChannel::Normal)-> + CreateSourceStream(nullptr); + mIndirectAudio = false; + mInited = true; +} + +void +nsSpeechTask::InitIndirectAudio() +{ + mIndirectAudio = true; mInited = true; } void nsSpeechTask::SetChosenVoiceURI(const nsAString& aUri) { mChosenVoiceURI = aUri; }
--- a/dom/media/webspeech/synth/nsSpeechTask.h +++ b/dom/media/webspeech/synth/nsSpeechTask.h @@ -43,17 +43,18 @@ public: virtual void ForceEnd(); float GetCurrentTime(); uint32_t GetCurrentCharOffset(); void SetSpeechSynthesis(SpeechSynthesis* aSpeechSynthesis); - void Init(ProcessedMediaStream* aStream); + void InitDirectAudio(); + void InitIndirectAudio(); void SetChosenVoiceURI(const nsAString& aUri); virtual void SetAudioOutputVolume(float aVolume); bool IsPreCanceled() { return mPreCanceled;
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp +++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp @@ -172,24 +172,16 @@ nsSynthVoiceRegistry::nsSynthVoiceRegist nsSynthVoiceRegistry::~nsSynthVoiceRegistry() { LOG(LogLevel::Debug, ("~nsSynthVoiceRegistry")); // mSpeechSynthChild's lifecycle is managed by the Content protocol. mSpeechSynthChild = nullptr; - if (mStream) { - if (!mStream->IsDestroyed()) { - mStream->Destroy(); - } - - mStream = nullptr; - } - mUriVoiceMap.Clear(); } nsSynthVoiceRegistry* nsSynthVoiceRegistry::GetInstance() { MOZ_ASSERT(NS_IsMainThread()); @@ -770,23 +762,18 @@ nsSynthVoiceRegistry::SpeakImpl(VoiceDat aRate, aPitch)); SpeechServiceType serviceType; DebugOnly<nsresult> rv = aVoice->mService->GetServiceType(&serviceType); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to get speech service type"); if (serviceType == nsISpeechService::SERVICETYPE_INDIRECT_AUDIO) { - aTask->Init(nullptr); + aTask->InitIndirectAudio(); } else { - if (!mStream) { - mStream = - MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, - AudioChannel::Normal)->CreateTrackUnionStream(nullptr); - } - aTask->Init(mStream); + aTask->InitDirectAudio(); } aVoice->mService->Speak(aText, aVoice->mUri, aVolume, aRate, aPitch, aTask); } } // namespace dom } // namespace mozilla
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.h +++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.h @@ -90,18 +90,16 @@ private: nsTArray<nsRefPtr<VoiceData>> mVoices; nsTArray<nsRefPtr<VoiceData>> mDefaultVoices; nsRefPtrHashtable<nsStringHashKey, VoiceData> mUriVoiceMap; SpeechSynthesisChild* mSpeechSynthChild; - nsRefPtr<ProcessedMediaStream> mStream; - bool mUseGlobalQueue; nsTArray<nsRefPtr<GlobalQueueItem>> mGlobalQueue; bool mIsSpeaking; }; } // namespace dom
--- a/dom/svg/SVGSVGElement.cpp +++ b/dom/svg/SVGSVGElement.cpp @@ -506,20 +506,20 @@ SVGSVGElement::SetCurrentScaleTranslate( // now dispatch the appropriate event if we are the root element nsIDocument* doc = GetUncomposedDoc(); if (doc) { nsCOMPtr<nsIPresShell> presShell = doc->GetShell(); if (presShell && IsRoot()) { nsEventStatus status = nsEventStatus_eIgnore; if (mPreviousScale != mCurrentScale) { - InternalSVGZoomEvent svgZoomEvent(true, NS_SVG_ZOOM); + InternalSVGZoomEvent svgZoomEvent(true, eSVGZoom); presShell->HandleDOMEventWithTarget(this, &svgZoomEvent, &status); } else { - WidgetEvent svgScrollEvent(true, NS_SVG_SCROLL); + WidgetEvent svgScrollEvent(true, eSVGScroll); presShell->HandleDOMEventWithTarget(this, &svgScrollEvent, &status); } InvalidateTransformNotifyFrame(); } } } void @@ -585,17 +585,17 @@ SVGSVGElement::IsAttributeMapped(const n } //---------------------------------------------------------------------- // nsIContent methods: nsresult SVGSVGElement::PreHandleEvent(EventChainPreVisitor& aVisitor) { - if (aVisitor.mEvent->mMessage == NS_SVG_LOAD) { + if (aVisitor.mEvent->mMessage == eSVGLoad) { if (mTimedDocumentRoot) { mTimedDocumentRoot->Begin(); // Set 'resample needed' flag, so that if any script calls a DOM method // that requires up-to-date animations before our first sample callback, // we'll force a synchronous sample. AnimationNeedsResample(); } }
--- a/dom/svg/SVGZoomEvent.cpp +++ b/dom/svg/SVGZoomEvent.cpp @@ -26,17 +26,17 @@ NS_IMPL_RELEASE_INHERITED(SVGZoomEvent, NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGZoomEvent) NS_INTERFACE_MAP_END_INHERITING(UIEvent) SVGZoomEvent::SVGZoomEvent(EventTarget* aOwner, nsPresContext* aPresContext, InternalSVGZoomEvent* aEvent) : UIEvent(aOwner, aPresContext, - aEvent ? aEvent : new InternalSVGZoomEvent(false, NS_SVG_ZOOM)) + aEvent ? aEvent : new InternalSVGZoomEvent(false, eSVGZoom)) , mPreviousScale(0) , mNewScale(0) { if (aEvent) { mEventIsInternal = false; } else { mEventIsInternal = true;
--- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -1852,16 +1852,19 @@ nsEditor::NotifyEditorObservers(Notifica if (!mDispatchInputEvent) { return; } FireInputEvent(); break; case eNotifyEditorObserversOfBefore: + if (NS_WARN_IF(mIsInEditAction)) { + break; + } mIsInEditAction = true; for (auto& observer : observers) { observer->BeforeEditAction(); } break; case eNotifyEditorObserversOfCancel: mIsInEditAction = false; for (auto& observer : observers) {
--- a/editor/libeditor/nsPlaintextEditor.cpp +++ b/editor/libeditor/nsPlaintextEditor.cpp @@ -880,20 +880,21 @@ nsPlaintextEditor::UpdateIMEComposition( rv = InsertText(compositionChangeEvent->mData); if (caretP) { caretP->SetSelection(selection); } } // If still composing, we should fire input event via observer. - // Note that if committed, we don't need to notify it since it will be - // notified at followed compositionend event. + // Note that if the composition will be committed by the following + // compositionend event, we don't need to notify editor observes of this + // change. // NOTE: We must notify after the auto batch will be gone. - if (IsIMEComposing()) { + if (!compositionChangeEvent->IsFollowedByCompositionEnd()) { NotifyEditorObservers(eNotifyEditorObserversOfEnd); } return rv; } already_AddRefed<nsIContent> nsPlaintextEditor::GetInputEventTargetContent()
--- a/gfx/harfbuzz/src/Makefile.am +++ b/gfx/harfbuzz/src/Makefile.am @@ -15,16 +15,18 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-int # Convenience targets: lib: $(BUILT_SOURCES) libharfbuzz.la lib_LTLIBRARIES = libharfbuzz.la HBCFLAGS = HBLIBS = +HBNONPCLIBS = +HBDEPS = HBSOURCES = \ hb-atomic-private.hh \ hb-blob.cc \ hb-buffer-deserialize-json.hh \ hb-buffer-deserialize-text.hh \ hb-buffer-private.hh \ hb-buffer-serialize.cc \ hb-buffer.cc \ @@ -34,16 +36,17 @@ HBSOURCES = \ hb-face.cc \ hb-font-private.hh \ hb-font.cc \ hb-mutex-private.hh \ hb-object-private.hh \ hb-open-file-private.hh \ hb-open-type-private.hh \ hb-ot-cmap-table.hh \ + hb-ot-glyf-table.hh \ hb-ot-head-table.hh \ hb-ot-hhea-table.hh \ hb-ot-hmtx-table.hh \ hb-ot-maxp-table.hh \ hb-ot-name-table.hh \ hb-ot-tag.cc \ hb-private.hh \ hb-set-private.hh \ @@ -88,16 +91,17 @@ HBSOURCES += \ hb-ot-layout-gsub-table.hh \ hb-ot-layout-jstf-table.hh \ hb-ot-layout-private.hh \ hb-ot-map.cc \ hb-ot-map-private.hh \ hb-ot-shape.cc \ hb-ot-shape-complex-arabic.cc \ hb-ot-shape-complex-arabic-fallback.hh \ + hb-ot-shape-complex-arabic-private.hh \ hb-ot-shape-complex-arabic-table.hh \ hb-ot-shape-complex-arabic-win1256.hh \ hb-ot-shape-complex-default.cc \ hb-ot-shape-complex-hangul.cc \ hb-ot-shape-complex-hebrew.cc \ hb-ot-shape-complex-indic.cc \ hb-ot-shape-complex-indic-machine.hh \ hb-ot-shape-complex-indic-private.hh \ @@ -127,65 +131,75 @@ HBHEADERS += \ endif if HAVE_FALLBACK HBSOURCES += hb-fallback-shape.cc endif if HAVE_PTHREAD HBCFLAGS += $(PTHREAD_CFLAGS) -HBLIBS += $(PTHREAD_LIBS) +HBNONPCLIBS += $(PTHREAD_LIBS) endif if HAVE_GLIB HBCFLAGS += $(GLIB_CFLAGS) HBLIBS += $(GLIB_LIBS) +HBDEPS += $(GLIB_DEPS) HBSOURCES += hb-glib.cc HBHEADERS += hb-glib.h endif if HAVE_FREETYPE HBCFLAGS += $(FREETYPE_CFLAGS) HBLIBS += $(FREETYPE_LIBS) +# XXX +# The following creates a recursive dependency on FreeType if FreeType is +# built with HarfBuzz support enabled. Newer pkg-config handles that just +# fine but pkg-config 0.26 as shipped in Ubuntu 14.04 crashes. Remove +# in a year or two, or otherwise work around it... +#HBDEPS += $(FREETYPE_DEPS) HBSOURCES += hb-ft.cc HBHEADERS += hb-ft.h endif if HAVE_GRAPHITE2 HBCFLAGS += $(GRAPHITE2_CFLAGS) HBLIBS += $(GRAPHITE2_LIBS) +HBDEPS += $(GRAPHITE2_DEPS) HBSOURCES += hb-graphite2.cc HBHEADERS += hb-graphite2.h endif if HAVE_UNISCRIBE HBCFLAGS += $(UNISCRIBE_CFLAGS) -HBLIBS += $(UNISCRIBE_LIBS) +HBNONPCLIBS += $(UNISCRIBE_LIBS) HBSOURCES += hb-uniscribe.cc HBHEADERS += hb-uniscribe.h endif if HAVE_CORETEXT HBCFLAGS += $(CORETEXT_CFLAGS) -HBLIBS += $(CORETEXT_LIBS) +HBNONPCLIBS += $(CORETEXT_LIBS) HBSOURCES += hb-coretext.cc HBHEADERS += hb-coretext.h endif if HAVE_UCDN SUBDIRS += hb-ucdn HBCFLAGS += -I$(srcdir)/hb-ucdn HBLIBS += hb-ucdn/libhb-ucdn.la HBSOURCES += hb-ucdn.cc endif DIST_SUBDIRS += hb-ucdn # Put the library together +HBLIBS += $(HBNONPCLIBS) + if OS_WIN32 export_symbols = -export-symbols harfbuzz.def harfbuzz_def_dependency = harfbuzz.def libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS) else # Use a C linker for GCC, not C++; Don't link to libstdc++ if HAVE_GCC libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS) @@ -250,31 +264,33 @@ EXTRA_DIST += \ %.pc: %.pc.in $(top_builddir)/config.status $(AM_V_GEN) \ $(SED) -e 's@%prefix%@$(prefix)@g' \ -e 's@%exec_prefix%@$(exec_prefix)@g' \ -e 's@%libdir%@$(libdir)@g' \ -e 's@%includedir%@$(includedir)@g' \ + -e 's@%libs_private%@$(HBNONPCLIBS)@g' \ + -e 's@%requires_private%@$(HBDEPS)@g' \ -e 's@%VERSION%@$(VERSION)@g' \ "$<" > "$@" \ || ($(RM) "$@"; false) CLEANFILES += $(pkgconfig_DATA) CLEANFILES += harfbuzz.def harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS) $(AM_V_GEN) (echo EXPORTS; \ (cat $^ || echo 'hb_ERROR ()' ) | \ $(EGREP) '^hb_.* \(' | \ sed -e 's/ (.*//' | \ LANG=C sort; \ - echo LIBRARY libharfbuzz-$(HB_VERSION_MAJOR).dll; \ + echo LIBRARY libharfbuzz-0.dll; \ ) >"$@" @ ! grep -q hb_ERROR "$@" \ || ($(RM) "$@"; false) GENERATORS = \ gen-arabic-table.py \ gen-indic-table.py \ @@ -365,17 +381,17 @@ TESTS_ENVIRONMENT = \ MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ HBSOURCES="$(HBSOURCES)" \ HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \ $(NULL) if HAVE_INTROSPECTION -include $(INTROSPECTION_MAKEFILE) -INTROSPECTION_GIRS = HarfBuzz-$(HB_VERSION_MAJOR).0.gir # What does the 0 mean anyway?! +INTROSPECTION_GIRS = HarfBuzz-0.0.gir # What does the 0 mean anyway?! INTROSPECTION_SCANNER_ARGS = -I$(srcdir) -n hb --identifier-prefix=hb_ --warn-all INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) INTROSPECTION_SCANNER_ENV = CC="$(CC)" HarfBuzz-0.0.gir: libharfbuzz.la libharfbuzz-gobject.la HarfBuzz_0_0_gir_INCLUDES = GObject-2.0 HarfBuzz_0_0_gir_CFLAGS = \ $(INCLUDES) \
--- a/gfx/harfbuzz/src/harfbuzz.pc.in +++ b/gfx/harfbuzz/src/harfbuzz.pc.in @@ -3,9 +3,11 @@ exec_prefix=%exec_prefix% libdir=%libdir% includedir=%includedir% Name: harfbuzz Description: HarfBuzz text shaping library Version: %VERSION% Libs: -L${libdir} -lharfbuzz +Libs.private: %libs_private% +Requires.private: %requires_private% Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-buffer-private.hh +++ b/gfx/harfbuzz/src/hb-buffer-private.hh @@ -196,16 +196,18 @@ struct hb_buffer_t { HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out); HB_INTERNAL bool shift_forward (unsigned int count); typedef long scratch_buffer_t; HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size); inline void clear_context (unsigned int side) { context_len[side] = 0; } + + HB_INTERNAL void sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)); }; #define HB_BUFFER_XALLOCATE_VAR(b, func, var, owner) \ b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \ sizeof (b->info[0].var), owner) #define HB_BUFFER_ALLOCATE_VAR(b, var) \ HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var (), #var)
--- a/gfx/harfbuzz/src/hb-buffer-serialize.cc +++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc @@ -94,17 +94,18 @@ static unsigned int unsigned int end, char *buf, unsigned int buf_size, unsigned int *buf_consumed, hb_font_t *font, hb_buffer_serialize_flags_t flags) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); - hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + NULL : hb_buffer_get_glyph_positions (buffer, NULL); *buf_consumed = 0; for (unsigned int i = start; i < end; i++) { char b[1024]; char *p = b; /* In the following code, we know b is large enough that no overflow can happen. */ @@ -139,16 +140,26 @@ static unsigned int if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", pos[i].x_offset, pos[i].y_offset); p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d", pos[i].x_advance, pos[i].y_advance); } + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"xb\":%d,\"yb\":%d", + extents.x_bearing, extents.y_bearing)); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"w\":%d,\"h\":%d", + extents.width, extents.height)); + } + *p++ = '}'; unsigned int l = p - b; if (buf_size > l) { memcpy (buf, b, l); buf += l; buf_size -= l; @@ -167,17 +178,18 @@ static unsigned int unsigned int end, char *buf, unsigned int buf_size, unsigned int *buf_consumed, hb_font_t *font, hb_buffer_serialize_flags_t flags) { hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL); - hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL); + hb_glyph_position_t *pos = (flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS) ? + NULL : hb_buffer_get_glyph_positions (buffer, NULL); *buf_consumed = 0; for (unsigned int i = start; i < end; i++) { char b[1024]; char *p = b; /* In the following code, we know b is large enough that no overflow can happen. */ @@ -203,16 +215,23 @@ static unsigned int p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset)); *p++ = '+'; p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance)); if (pos[i].y_advance) p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance)); } + if (flags & HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS) + { + hb_glyph_extents_t extents; + hb_font_get_glyph_extents(font, info[i].codepoint, &extents); + p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "<%d,%d,%d,%d>", extents.x_bearing, extents.y_bearing, extents.width, extents.height)); + } + unsigned int l = p - b; if (buf_size > l) { memcpy (buf, b, l); buf += l; buf_size -= l; *buf_consumed += l; *buf = '\0';
--- a/gfx/harfbuzz/src/hb-buffer.cc +++ b/gfx/harfbuzz/src/hb-buffer.cc @@ -1631,26 +1631,26 @@ normalize_glyphs_cluster (hb_buffer_t *b } if (backward) { /* Transfer all cluster advance to the last glyph. */ pos[end - 1].x_advance = total_x_advance; pos[end - 1].y_advance = total_y_advance; - hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); } else { /* Transfer all cluster advance to the first glyph. */ pos[start].x_advance += total_x_advance; pos[start].y_advance += total_y_advance; for (unsigned int i = start + 1; i < end; i++) { pos[i].x_offset -= total_x_advance; pos[i].y_offset -= total_y_advance; } - hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); } } /** * hb_buffer_normalize_glyphs: * @buffer: a buffer. * * @@ -1673,8 +1673,29 @@ hb_buffer_normalize_glyphs (hb_buffer_t unsigned int end; for (end = start + 1; end < count; end++) if (info[start].cluster != info[end].cluster) { normalize_glyphs_cluster (buffer, start, end, backward); start = end; } normalize_glyphs_cluster (buffer, start, end, backward); } + +void +hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) +{ + assert (!have_positions); + for (unsigned int i = start + 1; i < end; i++) + { + unsigned int j = i; + while (j > start && compar (&info[j - 1], &info[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + merge_clusters (j, i + 1); + { + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); + info[j] = t; + } + } +}
--- a/gfx/harfbuzz/src/hb-buffer.h +++ b/gfx/harfbuzz/src/hb-buffer.h @@ -318,17 +318,18 @@ hb_buffer_normalize_glyphs (hb_buffer_t /* * Serialize */ typedef enum { /*< flags >*/ HB_BUFFER_SERIALIZE_FLAG_DEFAULT = 0x00000000u, HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS = 0x00000001u, HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS = 0x00000002u, - HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u + HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES = 0x00000004u, + HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS = 0x00000008u } hb_buffer_serialize_flags_t; typedef enum { HB_BUFFER_SERIALIZE_FORMAT_TEXT = HB_TAG('T','E','X','T'), HB_BUFFER_SERIALIZE_FORMAT_JSON = HB_TAG('J','S','O','N'), HB_BUFFER_SERIALIZE_FORMAT_INVALID = HB_TAG_NONE } hb_buffer_serialize_format_t;
--- a/gfx/harfbuzz/src/hb-coretext.cc +++ b/gfx/harfbuzz/src/hb-coretext.cc @@ -151,16 +151,17 @@ hb_coretext_shaper_font_data_t * hb_coretext_shaper_font_data_t *data = (hb_coretext_shaper_font_data_t *) calloc (1, sizeof (hb_coretext_shaper_font_data_t)); if (unlikely (!data)) return NULL; hb_face_t *face = font->face; hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); /* Choose a CoreText font size and calculate multipliers to convert to HarfBuzz space. */ + /* TODO: use upem instead of 36? */ CGFloat font_size = 36.; /* Default... */ /* No idea if the following is even a good idea. */ if (font->y_ppem) font_size = font->y_ppem; if (font_size < 0) font_size = -font_size; data->x_mult = (CGFloat) font->x_scale / font_size; @@ -910,18 +911,18 @@ retry: } if (buffer->unicode->is_default_ignorable (ch)) continue; info->codepoint = notdef; info->cluster = log_clusters[j]; info->mask = advance; - info->var1.u32 = x_offset; - info->var2.u32 = y_offset; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; info++; buffer->len++; } if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) buffer->reverse_range (old_len, buffer->len); advances_so_far += run_advance; continue; @@ -997,72 +998,82 @@ retry: for (unsigned int j = 0; j < num_glyphs; j++) { double advance; if (likely (j + 1 < num_glyphs)) advance = positions[j + 1].x - positions[j].x; else /* last glyph */ advance = run_advance - (positions[j].x - positions[0].x); info->mask = advance * x_mult; - info->var1.u32 = x_offset; - info->var2.u32 = positions[j].y * y_mult; + info->var1.i32 = x_offset; + info->var2.i32 = positions[j].y * y_mult; info++; } } else { hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult; for (unsigned int j = 0; j < num_glyphs; j++) { double advance; if (likely (j + 1 < num_glyphs)) advance = positions[j + 1].y - positions[j].y; else /* last glyph */ advance = run_advance - (positions[j].y - positions[0].y); info->mask = advance * y_mult; - info->var1.u32 = positions[j].x * x_mult; - info->var2.u32 = y_offset; + info->var1.i32 = positions[j].x * x_mult; + info->var2.i32 = y_offset; info++; } } SCRATCH_RESTORE(); advances_so_far += run_advance; } #undef SCRATCH_RESTORE #undef SCRATCH_SAVE #undef USE_PTR #undef ALLOCATE_ARRAY buffer->len += num_glyphs; } - /* Make sure all runs had the expected direction. */ - bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); - assert (bool (status_and & kCTRunStatusRightToLeft) == backward); - assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel, + * or if it does, it doesn't resepct it. So we get runs with wrong + * directions. As such, disable the assert... It wouldn't crash, but + * cursoring will be off... + * + * http://crbug.com/419769 + */ + if (0) + { + /* Make sure all runs had the expected direction. */ + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + assert (bool (status_and & kCTRunStatusRightToLeft) == backward); + assert (bool (status_or & kCTRunStatusRightToLeft) == backward); + } buffer->clear_positions (); unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; hb_glyph_position_t *pos = buffer->pos; if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) for (unsigned int i = 0; i < count; i++) { pos->x_advance = info->mask; - pos->x_offset = info->var1.u32; - pos->y_offset = info->var2.u32; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; info++, pos++; } else for (unsigned int i = 0; i < count; i++) { pos->y_advance = info->mask; - pos->x_offset = info->var1.u32; - pos->y_offset = info->var2.u32; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; info++, pos++; } /* Fix up clusters so that we never return out-of-order indices; * if core text has reordered glyphs, we'll merge them to the * beginning of the reordered cluster. CoreText is nice enough * to tell us whenever it has produced nonmonotonic results... * Note that we assume the input clusters were nonmonotonic to
--- a/gfx/harfbuzz/src/hb-font.h +++ b/gfx/harfbuzz/src/hb-font.h @@ -75,22 +75,23 @@ void hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); hb_bool_t hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); /* glyph extents */ +/* Note that height is negative in coordinate systems that grow up. */ typedef struct hb_glyph_extents_t { - hb_position_t x_bearing; - hb_position_t y_bearing; - hb_position_t width; - hb_position_t height; + hb_position_t x_bearing; /* left side of glyph from origin. */ + hb_position_t y_bearing; /* top side of glyph from origin. */ + hb_position_t width; /* distance from left to right side. */ + hb_position_t height; /* distance from top to bottom side. */ } hb_glyph_extents_t; /* func types */ typedef hb_bool_t (*hb_font_get_glyph_func_t) (hb_font_t *font, void *font_data, hb_codepoint_t unicode, hb_codepoint_t variation_selector, hb_codepoint_t *glyph,
--- a/gfx/harfbuzz/src/hb-object-private.hh +++ b/gfx/harfbuzz/src/hb-object-private.hh @@ -42,30 +42,32 @@ #ifndef HB_DEBUG_OBJECT #define HB_DEBUG_OBJECT (HB_DEBUG+0) #endif /* reference_count */ -#define HB_REFERENCE_COUNT_INVALID_VALUE -1 -#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT(HB_REFERENCE_COUNT_INVALID_VALUE)} +#define HB_REFERENCE_COUNT_INERT_VALUE -1 +#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD +#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT(HB_REFERENCE_COUNT_INERT_VALUE)} struct hb_reference_count_t { hb_atomic_int_t ref_count; inline void init (int v) { ref_count.set_unsafe (v); } inline int get_unsafe (void) const { return ref_count.get_unsafe (); } inline int inc (void) { return ref_count.inc (); } inline int dec (void) { return ref_count.dec (); } - inline void finish (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_INVALID_VALUE); } + inline void finish (void) { ref_count.set_unsafe (HB_REFERENCE_COUNT_POISON_VALUE); } - inline bool is_invalid (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INVALID_VALUE; } + inline bool is_inert (void) const { return ref_count.get_unsafe () == HB_REFERENCE_COUNT_INERT_VALUE; } + inline bool is_valid (void) const { return ref_count.get_unsafe () > 0; } }; /* user_data */ #define HB_USER_DATA_ARRAY_INIT {HB_MUTEX_INIT, HB_LOCKABLE_SET_INIT} struct hb_user_data_array_t { @@ -137,55 +139,64 @@ template <typename Type> static inline void hb_object_init (Type *obj) { obj->header.ref_count.init (1); obj->header.user_data.init (); } template <typename Type> static inline bool hb_object_is_inert (const Type *obj) { - return unlikely (obj->header.ref_count.is_invalid ()); + return unlikely (obj->header.ref_count.is_inert ()); +} +template <typename Type> +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); } template <typename Type> static inline Type *hb_object_reference (Type *obj) { hb_object_trace (obj, HB_FUNC); if (unlikely (!obj || hb_object_is_inert (obj))) return obj; + assert (hb_object_is_valid (obj)); obj->header.ref_count.inc (); return obj; } template <typename Type> static inline bool hb_object_destroy (Type *obj) { hb_object_trace (obj, HB_FUNC); if (unlikely (!obj || hb_object_is_inert (obj))) return false; + assert (hb_object_is_valid (obj)); if (obj->header.ref_count.dec () != 1) return false; obj->header.ref_count.finish (); /* Do this before user_data */ obj->header.user_data.finish (); return true; } template <typename Type> static inline bool hb_object_set_user_data (Type *obj, hb_user_data_key_t *key, void * data, hb_destroy_func_t destroy, hb_bool_t replace) { if (unlikely (!obj || hb_object_is_inert (obj))) return false; + assert (hb_object_is_valid (obj)); return obj->header.user_data.set (key, data, destroy, replace); } template <typename Type> static inline void *hb_object_get_user_data (Type *obj, hb_user_data_key_t *key) { if (unlikely (!obj || hb_object_is_inert (obj))) return NULL; + assert (hb_object_is_valid (obj)); return obj->header.user_data.get (key); } #endif /* HB_OBJECT_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-open-type-private.hh +++ b/gfx/harfbuzz/src/hb-open-type-private.hh @@ -531,16 +531,30 @@ struct Supplier /* * Int types */ template <typename Type, int Bytes> struct BEInt; template <typename Type> +struct BEInt<Type, 1> +{ + public: + inline void set (Type V) + { + v = V; + } + inline operator Type (void) const + { + return v; + } + private: uint8_t v; +}; +template <typename Type> struct BEInt<Type, 2> { public: inline void set (Type V) { v[0] = (V >> 8) & 0xFF; v[1] = (V ) & 0xFF; } @@ -613,17 +627,17 @@ struct IntType return TRACE_RETURN (likely (c->check_struct (this))); } protected: BEInt<Type, Size> v; public: DEFINE_SIZE_STATIC (Size); }; -typedef uint8_t BYTE; /* 8-bit unsigned integer. */ +typedef IntType<uint8_t , 1> BYTE; /* 8-bit unsigned integer. */ typedef IntType<uint16_t, 2> USHORT; /* 16-bit unsigned integer. */ typedef IntType<int16_t, 2> SHORT; /* 16-bit signed integer. */ typedef IntType<uint32_t, 4> ULONG; /* 32-bit unsigned integer. */ typedef IntType<int32_t, 4> LONG; /* 32-bit signed integer. */ typedef IntType<uint32_t, 3> UINT24; /* 24-bit unsigned integer. */ /* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */ typedef SHORT FWORD;
--- a/gfx/harfbuzz/src/hb-ot-font.cc +++ b/gfx/harfbuzz/src/hb-ot-font.cc @@ -26,16 +26,18 @@ #include "hb-private.hh" #include "hb-ot.h" #include "hb-font-private.hh" #include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-head-table.hh" #include "hb-ot-hhea-table.hh" #include "hb-ot-hmtx-table.hh" struct hb_ot_face_metrics_accelerator_t { unsigned int num_metrics; unsigned int num_advances; @@ -71,31 +73,104 @@ struct hb_ot_face_metrics_accelerator_t hb_blob_destroy (this->blob); } inline unsigned int get_advance (hb_codepoint_t glyph) const { if (unlikely (glyph >= this->num_metrics)) { /* If this->num_metrics is zero, it means we don't have the metrics table - * for this direction: return one EM. Otherwise, it means that the glyph - * index is out of bound: return zero. */ + * for this direction: return default advance. Otherwise, it means that the + * glyph index is out of bound: return zero. */ if (this->num_metrics) return 0; else return this->default_advance; } if (glyph >= this->num_advances) glyph = this->num_advances - 1; return this->table->longMetric[glyph].advance; } }; +struct hb_ot_face_glyf_accelerator_t +{ + bool short_offset; + unsigned int num_glyphs; + const OT::loca *loca; + const OT::glyf *glyf; + hb_blob_t *loca_blob; + hb_blob_t *glyf_blob; + unsigned int glyf_len; + + inline void init (hb_face_t *face) + { + hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (face->reference_table (HB_OT_TAG_head)); + const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob); + if ((unsigned int) head->indexToLocFormat > 1 || head->glyphDataFormat != 0) + { + /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */ + hb_blob_destroy (head_blob); + return; + } + this->short_offset = 0 == head->indexToLocFormat; + hb_blob_destroy (head_blob); + + this->loca_blob = OT::Sanitizer<OT::loca>::sanitize (face->reference_table (HB_OT_TAG_loca)); + this->loca = OT::Sanitizer<OT::loca>::lock_instance (this->loca_blob); + this->glyf_blob = OT::Sanitizer<OT::glyf>::sanitize (face->reference_table (HB_OT_TAG_glyf)); + this->glyf = OT::Sanitizer<OT::glyf>::lock_instance (this->glyf_blob); + + this->num_glyphs = MAX (1u, hb_blob_get_length (this->loca_blob) / (this->short_offset ? 2 : 4)) - 1; + this->glyf_len = hb_blob_get_length (this->glyf_blob); + } + + inline void fini (void) + { + hb_blob_destroy (this->loca_blob); + hb_blob_destroy (this->glyf_blob); + } + + inline bool get_extents (hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + if (unlikely (glyph >= this->num_glyphs)) + return false; + + unsigned int start_offset, end_offset; + if (this->short_offset) + { + start_offset = 2 * this->loca->u.shortsZ[glyph]; + end_offset = 2 * this->loca->u.shortsZ[glyph + 1]; + } + else + { + start_offset = this->loca->u.longsZ[glyph]; + end_offset = this->loca->u.longsZ[glyph + 1]; + } + + if (start_offset > end_offset || end_offset > this->glyf_len) + return false; + + if (end_offset - start_offset < OT::glyfGlyphHeader::static_size) + return true; /* Empty glyph; zero extents. */ + + const OT::glyfGlyphHeader &glyph_header = OT::StructAtOffset<OT::glyfGlyphHeader> (this->glyf, start_offset); + + extents->x_bearing = MIN (glyph_header.xMin, glyph_header.xMax); + extents->y_bearing = MAX (glyph_header.yMin, glyph_header.yMax); + extents->width = MAX (glyph_header.xMin, glyph_header.xMax) - extents->x_bearing; + extents->height = MIN (glyph_header.yMin, glyph_header.yMax) - extents->y_bearing; + + return true; + } +}; + struct hb_ot_face_cmap_accelerator_t { const OT::CmapSubtable *table; const OT::CmapSubtable *uvs_table; hb_blob_t *blob; inline void init (hb_face_t *face) { @@ -153,16 +228,17 @@ struct hb_ot_face_cmap_accelerator_t }; struct hb_ot_font_t { hb_ot_face_cmap_accelerator_t cmap; hb_ot_face_metrics_accelerator_t h_metrics; hb_ot_face_metrics_accelerator_t v_metrics; + hb_ot_face_glyf_accelerator_t glyf; }; static hb_ot_font_t * _hb_ot_font_create (hb_font_t *font) { hb_ot_font_t *ot_font = (hb_ot_font_t *) calloc (1, sizeof (hb_ot_font_t)); hb_face_t *face = font->face; @@ -170,16 +246,17 @@ static hb_ot_font_t * if (unlikely (!ot_font)) return NULL; unsigned int upem = face->get_upem (); ot_font->cmap.init (face); ot_font->h_metrics.init (face, HB_OT_TAG_hhea, HB_OT_TAG_hmtx, upem>>1); ot_font->v_metrics.init (face, HB_OT_TAG_vhea, HB_OT_TAG_vmtx, upem); /* TODO Can we do this lazily? */ + ot_font->glyf.init (face); return ot_font; } static void _hb_ot_font_destroy (hb_ot_font_t *ot_font) { ot_font->cmap.fini (); @@ -271,18 +348,23 @@ hb_ot_get_glyph_v_kerning (hb_font_t *fo static hb_bool_t hb_ot_get_glyph_extents (hb_font_t *font HB_UNUSED, void *font_data, hb_codepoint_t glyph, hb_glyph_extents_t *extents, void *user_data HB_UNUSED) { - /* TODO */ - return false; + const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; + bool ret = ot_font->glyf.get_extents (glyph, extents); + extents->x_bearing = font->em_scale_x (extents->x_bearing); + extents->y_bearing = font->em_scale_y (extents->y_bearing); + extents->width = font->em_scale_x (extents->width); + extents->height = font->em_scale_y (extents->height); + return ret; } static hb_bool_t hb_ot_get_glyph_contour_point (hb_font_t *font HB_UNUSED, void *font_data, hb_codepoint_t glyph, unsigned int point_index, hb_position_t *x,
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh +++ b/gfx/harfbuzz/src/hb-ot-head-table.hh @@ -133,19 +133,20 @@ struct head * Bits 7-15: Reserved (set to 0). */ USHORT lowestRecPPEM; /* Smallest readable size in pixels. */ SHORT fontDirectionHint; /* Deprecated (Set to 2). * 0: Fully mixed directional glyphs; * 1: Only strongly left to right; * 2: Like 1 but also contains neutrals; * -1: Only strongly right to left; * -2: Like -1 but also contains neutrals. */ + public: SHORT indexToLocFormat; /* 0 for short offsets, 1 for long. */ SHORT glyphDataFormat; /* 0 for current format. */ - public: + DEFINE_SIZE_STATIC (54); }; } /* namespace OT */ #endif /* HB_OT_HEAD_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh @@ -878,16 +878,19 @@ struct EntryExitRecord OffsetTo<Anchor> exitAnchor; /* Offset to ExitAnchor table--from * beginning of CursivePos * subtable--may be NULL */ public: DEFINE_SIZE_STATIC (4); }; +static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent); + struct CursivePosFormat1 { inline void collect_glyphs (hb_collect_glyphs_context_t *c) const { TRACE_COLLECT_GLYPHS (this); (this+coverage).add_coverage (c->input); } @@ -955,30 +958,49 @@ struct CursivePosFormat1 pos[j].y_advance = entry_y; break; case HB_DIRECTION_INVALID: default: break; } /* Cross-direction adjustment */ - if (c->lookup_props & LookupFlag::RightToLeft) { - pos[i].cursive_chain() = j - i; - if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) - pos[i].y_offset = entry_y - exit_y; - else - pos[i].x_offset = entry_x - exit_x; - } else { - pos[j].cursive_chain() = i - j; - if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) - pos[j].y_offset = exit_y - entry_y; - else - pos[j].x_offset = exit_x - entry_x; + + /* We attach child to parent (think graph theory and rooted trees whereas + * the root stays on baseline and each node aligns itself against its + * parent. + * + * Optimize things for the case of RightToLeft, as that's most common in + * Arabinc. */ + unsigned int child = i; + unsigned int parent = j; + hb_position_t x_offset = entry_x - exit_x; + hb_position_t y_offset = entry_y - exit_y; + if (!(c->lookup_props & LookupFlag::RightToLeft)) + { + unsigned int k = child; + child = parent; + parent = k; + x_offset = -x_offset; + y_offset = -y_offset; } + /* If child was already connected to someone else, walk through its old + * chain and reverse the link direction, such that the whole tree of its + * previous connection now attaches to new parent. Watch out for case + * where new parent is on the path from old chain... + */ + reverse_cursive_minor_offset (pos, child, c->direction, parent); + + pos[child].cursive_chain() = parent - child; + if (likely (HB_DIRECTION_IS_HORIZONTAL (c->direction))) + pos[child].y_offset = y_offset; + else + pos[child].x_offset = x_offset; + buffer->idx = j; return TRACE_RETURN (true); } inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this)); @@ -1478,16 +1500,40 @@ struct GPOS : GSUBGPOS return TRACE_RETURN (list.sanitize (c, this)); } public: DEFINE_SIZE_STATIC (10); }; static void +reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) +{ + unsigned int j = pos[i].cursive_chain(); + if (likely (!j)) + return; + + j += i; + + pos[i].cursive_chain() = 0; + + /* Stop if we see new parent in the chain. */ + if (j == new_parent) + return; + + reverse_cursive_minor_offset (pos, j, direction, new_parent); + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + pos[j].y_offset = -pos[i].y_offset; + else + pos[j].x_offset = -pos[i].x_offset; + + pos[j].cursive_chain() = i - j; +} +static void fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction) { unsigned int j = pos[i].cursive_chain(); if (likely (!j)) return; j += i; @@ -1563,18 +1609,21 @@ template <typename context_t> return l.dispatch (c); } /*static*/ inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) { const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos); const PosLookup &l = gpos.get_lookup (lookup_index); unsigned int saved_lookup_props = c->lookup_props; - c->set_lookup (l); + unsigned int saved_lookup_index = c->lookup_index; + c->set_lookup_index (lookup_index); + c->set_lookup_props (l.get_props ()); bool ret = l.dispatch (c); + c->set_lookup_index (saved_lookup_index); c->set_lookup_props (saved_lookup_props); return ret; } #undef attach_lookback #undef cursive_chain
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh @@ -1306,18 +1306,21 @@ template <typename context_t> return l.dispatch (c); } /*static*/ inline bool SubstLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index) { const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub); const SubstLookup &l = gsub.get_lookup (lookup_index); unsigned int saved_lookup_props = c->lookup_props; - c->set_lookup (l); + unsigned int saved_lookup_index = c->lookup_index; + c->set_lookup_index (lookup_index); + c->set_lookup_props (l.get_props ()); bool ret = l.dispatch (c); + c->set_lookup_index (saved_lookup_index); c->set_lookup_props (saved_lookup_props); return ret; } } /* namespace OT */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh +++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh @@ -261,17 +261,18 @@ struct hb_add_coverage_context_t #ifndef HB_DEBUG_APPLY #define HB_DEBUG_APPLY (HB_DEBUG+0) #endif #define TRACE_APPLY(this) \ hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \ (&c->debug_depth, c->get_name (), this, HB_FUNC, \ - "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint); + "idx %d gid %u lookup %d", \ + c->buffer->idx, c->buffer->cur().codepoint, (int) c->lookup_index); struct hb_apply_context_t { struct matcher_t { inline matcher_t (void) : lookup_props (0), ignore_zwnj (false), @@ -476,16 +477,17 @@ struct hb_apply_context_t hb_mask_t lookup_mask; bool auto_zwj; recurse_func_t recurse_func; unsigned int nesting_level_left; unsigned int lookup_props; const GDEF &gdef; bool has_glyph_classes; skipping_iterator_t iter_input, iter_context; + unsigned int lookup_index; unsigned int debug_depth; hb_apply_context_t (unsigned int table_index_, hb_font_t *font_, hb_buffer_t *buffer_) : table_index (table_index_), font (font_), face (font->face), buffer (buffer_), @@ -494,22 +496,23 @@ struct hb_apply_context_t auto_zwj (true), recurse_func (NULL), nesting_level_left (MAX_NESTING_LEVEL), lookup_props (0), gdef (*hb_ot_layout_from_face (face)->gdef), has_glyph_classes (gdef.has_glyph_classes ()), iter_input (), iter_context (), + lookup_index ((unsigned int) -1), debug_depth (0) {} inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; } inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; } inline void set_recurse_func (recurse_func_t func) { recurse_func = func; } - inline void set_lookup (const Lookup &l) { set_lookup_props (l.get_props ()); } + inline void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; iter_input.init (this, false); iter_context.init (this, true); } inline bool
--- a/gfx/harfbuzz/src/hb-ot-layout.cc +++ b/gfx/harfbuzz/src/hb-ot-layout.cc @@ -954,17 +954,17 @@ apply_string (OT::hb_apply_context_t *c, const typename Proxy::Lookup &lookup, const hb_ot_layout_lookup_accelerator_t &accel) { hb_buffer_t *buffer = c->buffer; if (unlikely (!buffer->len || !c->lookup_mask)) return; - c->set_lookup (lookup); + c->set_lookup_props (lookup.get_props ()); if (likely (!lookup.is_reverse ())) { /* in/out forward substitution/positioning */ if (Proxy::table_index == 0) buffer->clear_output (); buffer->idx = 0; @@ -1005,17 +1005,30 @@ inline void hb_ot_map_t::apply (const Pr unsigned int i = 0; OT::hb_apply_context_t c (table_index, font, buffer); c.set_recurse_func (Proxy::Lookup::apply_recurse_func); for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { const stage_map_t *stage = &stages[table_index][stage_index]; for (; i < stage->last_lookup; i++) { +#if 0 + char buf[4096]; + hb_buffer_serialize_glyphs (buffer, 0, buffer->len, + buf, sizeof (buf), NULL, + font, + HB_BUFFER_SERIALIZE_FORMAT_TEXT, + Proxy::table_index == 0 ? + HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS : + HB_BUFFER_SERIALIZE_FLAG_DEFAULT); + printf ("buf: [%s]\n", buf); +#endif + unsigned int lookup_index = lookups[table_index][i].index; + c.set_lookup_index (lookup_index); c.set_lookup_mask (lookups[table_index][i].mask); c.set_auto_zwj (lookups[table_index][i].auto_zwj); apply_string<Proxy> (&c, proxy.table.get_lookup (lookup_index), proxy.accels[lookup_index]); } if (stage->pause_func)
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh @@ -70,19 +70,19 @@ arabic_fallback_synthesize_lookup_single substitutes[num_glyphs].set (s_glyph); num_glyphs++; } if (!num_glyphs) return NULL; - /* Bubble-sort! + /* Bubble-sort or something equally good! * May not be good-enough for presidential candidate interviews, but good-enough for us... */ - hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); + hb_stable_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); OT::Supplier<OT::GlyphID> glyphs_supplier (glyphs, num_glyphs); OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs); /* Each glyph takes four bytes max, and there's some overhead. */ char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; OT::hb_serialize_context_t c (buf, sizeof (buf)); OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); @@ -121,17 +121,17 @@ arabic_fallback_synthesize_lookup_ligatu hb_codepoint_t first_glyph; if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) continue; first_glyphs[num_first_glyphs].set (first_glyph); ligature_per_first_glyph_count_list[num_first_glyphs] = 0; first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; num_first_glyphs++; } - hb_bubble_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]); + hb_stable_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]); /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ for (unsigned int i = 0; i < num_first_glyphs; i++) { unsigned int first_glyph_idx = first_glyphs_indirection[i]; for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++) {
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc @@ -200,21 +200,21 @@ preprocess_text_hangul (const hb_ot_shap * I didn't bother for now. */ if (start < end && end == buffer->out_len) { /* Tone mark follows a valid syllable; move it in front, unless it's zero width. */ buffer->next_glyph (); if (!is_zero_width_char (font, u)) { + buffer->merge_out_clusters (start, end + 1); hb_glyph_info_t *info = buffer->out_info; hb_glyph_info_t tone = info[end]; memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t)); info[start] = tone; - buffer->merge_out_clusters (start, end + 1); } } else { /* No valid syllable as base for tone mark; try to insert dotted circle. */ if (font->has_glyph (0x25CCu)) { hb_codepoint_t chars[2];
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc @@ -1007,30 +1007,34 @@ initial_reordering_consonant_syllable (c { /* Use syllable() for sort accounting temporarily. */ unsigned int syllable = info[start].syllable(); for (unsigned int i = start; i < end; i++) info[i].syllable() = i - start; /* Sit tight, rock 'n roll! */ - hb_bubble_sort (info + start, end - start, compare_indic_order); + hb_stable_sort (info + start, end - start, compare_indic_order); /* Find base again */ base = end; for (unsigned int i = start; i < end; i++) if (info[i].indic_position() == POS_BASE_C) { base = i; break; } /* Things are out-of-control for post base positions, they may shuffle * around like crazy. In old-spec mode, we move halants around, so in * that case merge all clusters after base. Otherwise, check the sort * order and merge as needed. - * For pre-base stuff, we handle cluster issues in final reordering. */ + * For pre-base stuff, we handle cluster issues in final reordering. + * + * We could use buffer->sort() for this, if there was no special + * reordering of pre-base stuff happening later... + */ if (indic_plan->is_old_spec || end - base > 127) buffer->merge_clusters (base, end); else { /* Note! syllable() is a one-byte field. */ for (unsigned int i = base; i < end; i++) if (info[i].syllable() != 255) { @@ -1399,22 +1403,27 @@ final_reordering_syllable (const hb_ot_s if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M) { /* Now go see if there's actually any matras... */ for (unsigned int i = new_pos; i > start; i--) if (info[i - 1].indic_position () == POS_PRE_M) { unsigned int old_pos = i - 1; + if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ + base--; + hb_glyph_info_t tmp = info[old_pos]; memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0])); info[new_pos] = tmp; - if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ - base--; + + /* Note: this merge_clusters() is intentionally *after* the reordering. + * Indic matra reordering is special and tricky... */ buffer->merge_clusters (new_pos, MIN (end, base + 1)); + new_pos--; } } else { for (unsigned int i = start; i < base; i++) if (info[i].indic_position () == POS_PRE_M) { buffer->merge_clusters (i, MIN (end, base + 1)); break; } @@ -1557,22 +1566,22 @@ final_reordering_syllable (const hb_ot_s new_reph_pos--; } } goto reph_move; } reph_move: { + /* Move */ buffer->merge_clusters (start, new_reph_pos + 1); - - /* Move */ hb_glyph_info_t reph = info[start]; memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0])); info[new_reph_pos] = reph; + if (start < base && base <= new_reph_pos) base--; } } /* o Reorder pre-base reordering consonants: * @@ -1635,20 +1644,22 @@ final_reordering_syllable (const hb_ot_s { /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ if (new_pos < end && is_joiner (info[new_pos])) new_pos++; } { unsigned int old_pos = i; + buffer->merge_clusters (new_pos, old_pos + 1); hb_glyph_info_t tmp = info[old_pos]; memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0])); info[new_pos] = tmp; + if (new_pos <= base && base < old_pos) base++; } } break; } }
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc @@ -386,19 +386,18 @@ initial_reordering_consonant_syllable (h pos = POS_AFTER_SUB; info[i].myanmar_position() = pos; continue; } info[i].myanmar_position() = pos; } } - buffer->merge_clusters (start, end); /* Sit tight, rock 'n roll! */ - hb_bubble_sort (info + start, end - start, compare_myanmar_order); + buffer->sort (start, end, compare_myanmar_order); } static void initial_reordering_syllable (const hb_ot_shape_plan_t *plan, hb_face_t *face, hb_buffer_t *buffer, unsigned int start, unsigned int end) {
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc @@ -348,17 +348,17 @@ preprocess_text_thai (const hb_ot_shape_ buffer->out_info + start, sizeof (buffer->out_info[0]) * (end - start - 2)); buffer->out_info[start] = t; } else { /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the * previous cluster. */ - if (start) + if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) buffer->merge_out_clusters (start - 1, end); } } buffer->swap_buffers (); /* If font has Thai GSUB, we are done. */ if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0]) do_thai_pua_shaping (plan, buffer, font);
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use.cc @@ -233,24 +233,16 @@ enum syllable_type_t { numeral_cluster, symbol_cluster, broken_cluster, }; #include "hb-ot-shape-complex-use-machine.hh" -static inline void -set_use_properties (hb_glyph_info_t &info) -{ - hb_codepoint_t u = info.codepoint; - info.use_category() = hb_use_get_categories (u); -} - - static void setup_masks_use (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, hb_font_t *font HB_UNUSED) { const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; /* Do this before allocating use_category(). */ @@ -439,20 +431,20 @@ reorder_syllable (hb_buffer_t *buffer, u if (FLAG_UNSAFE (info[i].use_category()) & (HALANT_FLAGS | BASE_FLAGS)) { /* If we hit a halant, move before it; otherwise it's a base: move to it's * place, and shift things in between backward. */ if (info[i].use_category() == USE_H) i--; + buffer->merge_clusters (start, i + 1); hb_glyph_info_t t = info[start]; memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0])); info[i] = t; - buffer->merge_clusters (start, i + 1); break; } } /* Move things back. */ unsigned int j = end; for (unsigned int i = start; i < end; i++) @@ -467,20 +459,20 @@ reorder_syllable (hb_buffer_t *buffer, u else j = i; } else if (((flag) & (FLAG (USE_VPre) | FLAG (USE_VMPre))) && /* Only move the first component of a MultipleSubst. */ 0 == _hb_glyph_info_get_lig_comp (&info[i]) && j < i) { + buffer->merge_clusters (j, i + 1); hb_glyph_info_t t = info[i]; memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0])); info[j] = t; - buffer->merge_clusters (j, i + 1); } } } static inline void insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font, hb_buffer_t *buffer)
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc +++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc @@ -339,25 +339,23 @@ void if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) continue; unsigned int end; for (end = i + 1; end < count; end++) if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0) break; - /* We are going to do a bubble-sort. Only do this if the - * sequence is short. Doing it on long sequences can result - * in an O(n^2) DoS. */ + /* We are going to do a O(n^2). Only do this if the sequence is short. */ if (end - i > 10) { i = end; continue; } - hb_bubble_sort (buffer->info + i, end - i, compare_combining_class); + buffer->sort (i, end, compare_combining_class); i = end; } if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE || mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED) return;
--- a/gfx/harfbuzz/src/hb-ot-shape.cc +++ b/gfx/harfbuzz/src/hb-ot-shape.cc @@ -297,25 +297,26 @@ hb_ensure_native_direction (hb_buffer_t * Since form_clusters() merged clusters already, we don't merge. */ unsigned int base = 0; unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) { if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) { - buffer->reverse_range (base, i); if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) buffer->merge_clusters (base, i); + buffer->reverse_range (base, i); + base = i; } } - buffer->reverse_range (base, count); if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) buffer->merge_clusters (base, count); + buffer->reverse_range (base, count); buffer->reverse (); buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); } } @@ -508,16 +509,18 @@ hb_ot_hide_default_ignorables (hb_ot_sha static inline void hb_ot_map_glyphs_fast (hb_buffer_t *buffer) { /* Normalization process sets up glyph_index(), we just copy it. */ unsigned int count = buffer->len; hb_glyph_info_t *info = buffer->info; for (unsigned int i = 0; i < count; i++) info[i].codepoint = info[i].glyph_index(); + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; } static inline void hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) { unsigned int count = c->buffer->len; hb_glyph_info_t *info = c->buffer->info; for (unsigned int i = 0; i < count; i++)
--- a/gfx/harfbuzz/src/hb-private.hh +++ b/gfx/harfbuzz/src/hb-private.hh @@ -850,52 +850,44 @@ hb_in_ranges (T u, T lo1, T hi1, T lo2, */ #define FLAG(x) (ASSERT_STATIC_EXPR_ZERO ((x) < 32) + (1U << (x))) #define FLAG_SAFE(x) (1U << (x)) #define FLAG_UNSAFE(x) ((x) < 32 ? FLAG_SAFE(x) : 0) #define FLAG_RANGE(x,y) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x)) template <typename T, typename T2> static inline void -hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) { - if (unlikely (!len)) - return; - - unsigned int k = len - 1; - do { - unsigned int new_k = 0; - - for (unsigned int j = 0; j < k; j++) - if (compar (&array[j], &array[j+1]) > 0) - { - { - T t; - t = array[j]; - array[j] = array[j + 1]; - array[j + 1] = t; - } - if (array2) - { - T2 t; - t = array2[j]; - array2[j] = array2[j + 1]; - array2[j + 1] = t; - } - - new_k = j; - } - k = new_k; - } while (k); + for (unsigned int i = 1; i < len; i++) + { + unsigned int j = i; + while (j && compar (&array[j - 1], &array[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + { + T t = array[i]; + memmove (&array[j + 1], &array[j], (i - j) * sizeof (T)); + array[j] = t; + } + if (array2) + { + T2 t = array2[i]; + memmove (&array2[j + 1], &array2[j], (i - j) * sizeof (T2)); + array2[j] = t; + } + } } template <typename T> static inline void -hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) { - hb_bubble_sort (array, len, compar, (int *) NULL); + hb_stable_sort (array, len, compar, (int *) NULL); } static inline hb_bool_t hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out) { /* Pain because we don't know whether s is nul-terminated. */ char buf[64]; len = MIN (ARRAY_LENGTH (buf) - 1, len);
--- a/gfx/harfbuzz/src/hb-uniscribe.cc +++ b/gfx/harfbuzz/src/hb-uniscribe.cc @@ -481,24 +481,26 @@ void * shaper font data */ struct hb_uniscribe_shaper_font_data_t { HDC hdc; LOGFONTW log_font; HFONT hfont; SCRIPT_CACHE script_cache; + double x_mult, y_mult; /* From LOGFONT space to HB space. */ }; static bool populate_log_font (LOGFONTW *lf, - hb_font_t *font) + hb_font_t *font, + unsigned int font_size) { memset (lf, 0, sizeof (*lf)); - lf->lfHeight = -font->y_scale; + lf->lfHeight = -font_size; lf->lfCharSet = DEFAULT_CHARSET; hb_face_t *face = font->face; hb_uniscribe_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face); memcpy (lf->lfFaceName, face_data->face_name, sizeof (lf->lfFaceName)); return true; @@ -508,19 +510,29 @@ hb_uniscribe_shaper_font_data_t * _hb_uniscribe_shaper_font_data_create (hb_font_t *font) { if (unlikely (!hb_uniscribe_shaper_face_data_ensure (font->face))) return NULL; hb_uniscribe_shaper_font_data_t *data = (hb_uniscribe_shaper_font_data_t *) calloc (1, sizeof (hb_uniscribe_shaper_font_data_t)); if (unlikely (!data)) return NULL; + int font_size = font->face->get_upem (); /* Default... */ + /* No idea if the following is even a good idea. */ + if (font->y_ppem) + font_size = font->y_ppem; + + if (font_size < 0) + font_size = -font_size; + data->x_mult = (double) font->x_scale / font_size; + data->y_mult = (double) font->y_scale / font_size; + data->hdc = GetDC (NULL); - if (unlikely (!populate_log_font (&data->log_font, font))) { + if (unlikely (!populate_log_font (&data->log_font, font, font_size))) { DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed"); _hb_uniscribe_shaper_font_data_destroy (data); return NULL; } data->hfont = CreateFontIndirectW (&data->log_font); if (unlikely (!data->hfont)) { DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed"); @@ -989,31 +1001,32 @@ retry: { hb_glyph_info_t *info = &buffer->info[buffer->len++]; info->codepoint = glyphs[i]; info->cluster = vis_clusters[i]; /* The rest is crap. Let's store position info there for now. */ info->mask = advances[i]; - info->var1.u32 = offsets[i].du; - info->var2.u32 = offsets[i].dv; + info->var1.i32 = offsets[i].du; + info->var2.i32 = offsets[i].dv; } /* Set glyph positions */ buffer->clear_positions (); + double x_mult = font_data->x_mult, y_mult = font_data->y_mult; for (unsigned int i = 0; i < glyphs_len; i++) { hb_glyph_info_t *info = &buffer->info[i]; hb_glyph_position_t *pos = &buffer->pos[i]; /* TODO vertical */ - pos->x_advance = info->mask; - pos->x_offset = backward ? -info->var1.u32 : info->var1.u32; - pos->y_offset = info->var2.u32; + pos->x_advance = x_mult * info->mask; + pos->x_offset = x_mult * (backward ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; } if (backward) hb_buffer_reverse (buffer); /* Wow, done! */ return true; }
--- a/gfx/harfbuzz/src/hb-version.h +++ b/gfx/harfbuzz/src/hb-version.h @@ -33,19 +33,19 @@ #include "hb-common.h" HB_BEGIN_DECLS #define HB_VERSION_MAJOR 1 #define HB_VERSION_MINOR 0 -#define HB_VERSION_MICRO 1 +#define HB_VERSION_MICRO 3 -#define HB_VERSION_STRING "1.0.1" +#define HB_VERSION_STRING "1.0.3" #define HB_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) void hb_version (unsigned int *major,
--- a/gfx/thebes/gfxMacPlatformFontList.h +++ b/gfx/thebes/gfxMacPlatformFontList.h @@ -62,16 +62,17 @@ protected: static void DestroyBlobFunc(void* aUserData); CGFontRef mFontRef; // owning reference to the CGFont, released on destruction bool mFontRefInitialized; bool mRequiresAAT; bool mIsCFF; bool mIsCFFInitialized; + nsTHashtable<nsUint32HashKey> mAvailableTables; }; class gfxMacPlatformFontList : public gfxPlatformFontList { public: static gfxMacPlatformFontList* PlatformFontList() { return static_cast<gfxMacPlatformFontList*>(sPlatformFontList); }
--- a/gfx/thebes/gfxMacPlatformFontList.mm +++ b/gfx/thebes/gfxMacPlatformFontList.mm @@ -370,30 +370,36 @@ MacOSFontEntry::GetFontTable(uint32_t aT } return nullptr; } bool MacOSFontEntry::HasFontTable(uint32_t aTableTag) { - nsAutoreleasePool localPool; + if (mAvailableTables.Count() == 0) { + nsAutoreleasePool localPool; - CGFontRef fontRef = GetFontRef(); - if (!fontRef) { - return false; + CGFontRef fontRef = GetFontRef(); + if (!fontRef) { + return false; + } + CFArrayRef tags = ::CGFontCopyTableTags(fontRef); + if (!tags) { + return false; + } + int numTags = (int) ::CFArrayGetCount(tags); + for (int t = 0; t < numTags; t++) { + uint32_t tag = (uint32_t)(uintptr_t)::CFArrayGetValueAtIndex(tags, t); + mAvailableTables.PutEntry(tag); + } + ::CFRelease(tags); } - CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag); - if (!tableData) { - return false; - } - - ::CFRelease(tableData); - return true; + return mAvailableTables.GetEntry(aTableTag); } void MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, FontListSizes* aSizes) const { aSizes->mFontListSize += aMallocSizeOf(this); AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
--- a/js/src/asmjs/AsmJSFrameIterator.cpp +++ b/js/src/asmjs/AsmJSFrameIterator.cpp @@ -245,17 +245,17 @@ GenerateProfilingEpilogue(MacroAssembler // and the async interrupt exit. Since activation.fp can be read at any // time and still points to the current frame, be careful to only update // sp after activation.fp has been repointed to the caller's frame. #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) masm.loadPtr(Address(masm.getStackPointer(), 0), scratch2); masm.storePtr(scratch2, Address(scratch, AsmJSActivation::offsetOfFP())); DebugOnly<uint32_t> prePop = masm.currentOffset(); masm.addToStackPtr(Imm32(sizeof(void *))); - MOZ_ASSERT(PostStorePrePopFP == masm.currentOffset() - prePop); + MOZ_ASSERT_IF(!masm.oom(), PostStorePrePopFP == masm.currentOffset() - prePop); #else masm.pop(Address(scratch, AsmJSActivation::offsetOfFP())); MOZ_ASSERT(PostStorePrePopFP == 0); #endif masm.bind(profilingReturn); masm.ret(); }
--- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -12381,17 +12381,18 @@ CheckModule(ExclusiveContext* cx, AsmJSP #endif m.startFunctionBodies(); ScopedJSDeletePtr<ModuleCompileResults> mcd; if (!CheckFunctions(m, &mcd)) return false; - m.finishFunctionBodies(&mcd); + if (!m.finishFunctionBodies(&mcd)) + return false; if (!CheckFuncPtrTables(m)) return false; if (!CheckModuleReturn(m)) return false; TokenKind tk;
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2918,17 +2918,18 @@ BytecodeEmitter::emitNumberOp(double dva return emit1(JSOP_ZERO); if (ival == 1) return emit1(JSOP_ONE); if ((int)(int8_t)ival == ival) return emit2(JSOP_INT8, uint8_t(int8_t(ival))); uint32_t u = uint32_t(ival); if (u < JS_BIT(16)) { - emitUint16Operand(JSOP_UINT16, u); + if (!emitUint16Operand(JSOP_UINT16, u)) + return false; } else if (u < JS_BIT(24)) { ptrdiff_t off; if (!emitN(JSOP_UINT24, 3, &off)) return false; SET_UINT24(code(off), u); } else { ptrdiff_t off; if (!emitN(JSOP_INT32, 4, &off))
--- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -477,16 +477,18 @@ class FullParseHandler ParseNode* makeGen = new_<NullaryNode>(PNK_GENERATOR, yieldPos); if (!makeGen) return false; MOZ_ASSERT(genName->getOp() == JSOP_GETNAME); genName->setOp(JSOP_SETNAME); genName->markAsAssigned(); ParseNode* genInit = newBinary(PNK_ASSIGN, genName, makeGen); + if (!genInit) + return false; ParseNode* initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit, JSOP_INITIALYIELD); if (!initialYield) return false; stmtList->prepend(initialYield); return true;
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/sharedbuf/typedarray-from-sharedtypedarray-with-overridden-length.js @@ -0,0 +1,23 @@ +if (!this.SharedArrayBuffer) + quit(0); + +// This would hit an assertion in debug builds due to an incorrect +// type guard in the code that copies data from STA to TA. + +// Original test case + +var x = new SharedInt32Array(4); +x.__proto__ = (function(){}); +new Uint8Array(x); // Should assert here + +// Supposedly equivalent test case, provoking the error directly + +var x = new SharedInt32Array(4); +Object.defineProperty(x, "length", { value: 0 }); +new Uint8Array(x); // Should assert here + +// Derived test case - should not tickle the bug, though. + +var x = new SharedInt32Array(4); +Object.defineProperty(x, "length", { value: 1 << 20 }); +new Uint8Array(x);
--- a/js/src/jit/AtomicOperations-inl.h +++ b/js/src/jit/AtomicOperations-inl.h @@ -7,17 +7,17 @@ #ifndef jit_AtomicOperations_inl_h #define jit_AtomicOperations_inl_h #if defined(JS_CODEGEN_ARM) # include "jit/arm/AtomicOperations-arm.h" #elif defined(JS_CODEGEN_ARM64) # include "jit/arm64/AtomicOperations-arm64.h" #elif defined(JS_CODEGEN_MIPS32) -# include "jit/mips32/AtomicOperations-mips32.h" +# include "jit/mips-shared/AtomicOperations-mips-shared.h" #elif defined(JS_CODEGEN_NONE) # include "jit/none/AtomicOperations-none.h" #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) # include "jit/x86-shared/AtomicOperations-x86-shared.h" #else # error "Atomic operations must be defined for this platform" #endif
--- a/js/src/jit/JitCompartment.h +++ b/js/src/jit/JitCompartment.h @@ -401,19 +401,23 @@ class JitCompartment } JitCode* getStubCode(uint32_t key) { ICStubCodeMap::AddPtr p = stubCodes_->lookupForAdd(key); if (p) return p->value(); return nullptr; } - bool putStubCode(uint32_t key, Handle<JitCode*> stubCode) { + bool putStubCode(JSContext* cx, uint32_t key, Handle<JitCode*> stubCode) { MOZ_ASSERT(stubCode); - return stubCodes_->putNew(key, stubCode.get()); + if (!stubCodes_->putNew(key, stubCode.get())) { + ReportOutOfMemory(cx); + return false; + } + return true; } void initBaselineCallReturnAddr(void* addr, bool constructing) { MOZ_ASSERT(baselineCallReturnAddrs_[constructing] == nullptr); baselineCallReturnAddrs_[constructing] = addr; } void* baselineCallReturnAddr(bool constructing) { MOZ_ASSERT(baselineCallReturnAddrs_[constructing] != nullptr); return baselineCallReturnAddrs_[constructing];
--- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -723,17 +723,17 @@ ICStubCompiler::getStubCode() if (!postGenerateStubCode(masm, newStubCode)) return nullptr; // All barriers are emitted off-by-default, enable them if needed. if (cx->zone()->needsIncrementalBarrier()) newStubCode->togglePreBarriers(true); // Cache newly compiled stubcode. - if (!comp->putStubCode(stubKey, newStubCode)) + if (!comp->putStubCode(cx, stubKey, newStubCode)) return nullptr; MOZ_ASSERT(entersStubFrame_ == ICStub::CanMakeCalls(kind)); MOZ_ASSERT(!inStubFrame_); #ifdef JS_ION_PERF writePerfSpewerJitCodeProfile(newStubCode, "BaselineIC"); #endif
--- a/js/src/jit/Snapshots.cpp +++ b/js/src/jit/Snapshots.cpp @@ -656,18 +656,20 @@ SnapshotWriter::add(const RValueAllocati { MOZ_ASSERT(allocMap_.initialized()); uint32_t offset; RValueAllocMap::AddPtr p = allocMap_.lookupForAdd(alloc); if (!p) { offset = allocWriter_.length(); alloc.write(allocWriter_); - if (!allocMap_.add(p, alloc, offset)) + if (!allocMap_.add(p, alloc, offset)) { + allocWriter_.setOOM(); return false; + } } else { offset = p->value(); } if (JitSpewEnabled(JitSpew_IonSnapshots)) { JitSpewHeader(JitSpew_IonSnapshots); Fprinter& out = JitSpewPrinter(); out.printf(" slot %u (%d): ", allocWritten_, offset);
--- a/js/src/jit/arm/Architecture-arm.h +++ b/js/src/jit/arm/Architecture-arm.h @@ -234,47 +234,47 @@ class FloatRegisters d27, d28, d29, d30, d31, invalid_freg }; - typedef FPRegisterID Code; + typedef uint32_t Code; typedef FPRegisterID Encoding; // Content spilled during bailouts. union RegisterContent { double d; }; - static const char* GetDoubleName(Code code) { + static const char* GetDoubleName(Encoding code) { static const char * const Names[] = { "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"}; return Names[code]; } - static const char* GetSingleName(Code code) { + static const char* GetSingleName(Encoding code) { static const char * const Names[] = { "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15", "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31"}; return Names[code]; } static const char* GetName(uint32_t i) { MOZ_ASSERT(i < Total); - return GetName(Code(i)); + return GetName(Encoding(i)); } static Code FromName(const char* name); - static const Code Invalid = invalid_freg; + static const Encoding Invalid = invalid_freg; static const uint32_t Total = 48; static const uint32_t TotalDouble = 16; static const uint32_t TotalSingle = 32; static const uint32_t Allocatable = 45; // There are only 32 places that we can put values. static const uint32_t TotalPhys = 32; static uint32_t ActualTotalPhys(); @@ -427,35 +427,35 @@ class VFPRegister MOZ_ASSERT(!_isInvalid && !_isMissing); // This should only be used in areas where we only have doubles and // singles. MOZ_ASSERT(isFloat()); return Code(code_ | (kind << 5)); } Encoding encoding() const { MOZ_ASSERT(!_isInvalid && !_isMissing); - return Code(code_ | (kind << 5)); + return Encoding(code_); } uint32_t id() const { return code_; } static VFPRegister FromCode(uint32_t i) { uint32_t code = i & 31; uint32_t kind = i >> 5; return VFPRegister(code, RegType(kind)); } bool volatile_() const { if (isDouble()) return !!((1 << (code_ >> 1)) & FloatRegisters::VolatileMask); return !!((1 << code_) & FloatRegisters::VolatileMask); } const char* name() const { if (isDouble()) - return FloatRegisters::GetDoubleName(Code(code_)); - return FloatRegisters::GetSingleName(Code(code_)); + return FloatRegisters::GetDoubleName(Encoding(code_)); + return FloatRegisters::GetSingleName(Encoding(code_)); } bool operator != (const VFPRegister& other) const { return other.kind != kind || code_ != other.code_; } bool aliases(const VFPRegister& other) { if (kind == other.kind) return code_ == other.code_; return doubleOverlay() == other.doubleOverlay();
--- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -471,17 +471,17 @@ private: while (iter.more()) { startFloatTransferM(ls, rm, mode, WriteBack); int32_t reg = (*iter).code(); do { offset += delta; if ((*iter).isDouble()) offset += delta; transferFloatReg(*iter); - } while ((++iter).more() && (*iter).code() == (reg += sign)); + } while ((++iter).more() && int32_t((*iter).code()) == (reg += sign)); finishFloatTransfer(); } return offset; } }; class MacroAssembler;
new file mode 100644 --- /dev/null +++ b/js/src/jit/mips-shared/AtomicOperations-mips-shared.h @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* For documentation, see jit/AtomicOperations.h */ + +#ifndef jit_mips_shared_AtomicOperations_mips_shared_h +#define jit_mips_shared_AtomicOperations_mips_shared_h + +#include "jit/AtomicOperations.h" + +#if defined(__clang__) || defined(__GNUC__) + +// The default implementation tactic for gcc/clang is to use the newer +// __atomic intrinsics added for use in C++11 <atomic>. Where that +// isn't available, we use GCC's older __sync functions instead. +// +// ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS is kept as a backward +// compatible option for older compilers: enable this to use GCC's old +// __sync functions instead of the newer __atomic functions. This +// will be required for GCC 4.6.x and earlier, and probably for Clang +// 3.1, should we need to use those versions. + +//#define ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + +inline bool +js::jit::AtomicOperations::isLockfree8() +{ +# ifndef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int8_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int16_t), 0)); + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int32_t), 0)); +# if _MIPS_SIM == _ABI64 + MOZ_ASSERT(__atomic_always_lock_free(sizeof(int64_t), 0)); +# endif + return true; +# else + return false; +# endif +} + +inline void +js::jit::AtomicOperations::fenceSeqCst() +{ +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); +# else + __atomic_thread_fence(__ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::loadSeqCst(T* addr) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); + T v = *addr; + __sync_synchronize(); +# else + T v; + __atomic_load(addr, &v, __ATOMIC_SEQ_CST); +# endif + return v; +} + +template<typename T> +inline void +js::jit::AtomicOperations::storeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_synchronize(); + *addr = val; + __sync_synchronize(); +# else + __atomic_store(addr, &val, __ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_val_compare_and_swap(addr, oldval, newval); +# else + __atomic_compare_exchange(addr, &oldval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return oldval; +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_add(addr, val); +# else + return __atomic_fetch_add(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_sub(addr, val); +# else + return __atomic_fetch_sub(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_and(addr, val); +# else + return __atomic_fetch_and(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_or(addr, val); +# else + return __atomic_fetch_or(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) +{ + static_assert(sizeof(T) <= 4, "not available for 8-byte values yet"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + return __sync_fetch_and_xor(addr, val); +# else + return __atomic_fetch_xor(addr, val, __ATOMIC_SEQ_CST); +# endif +} + +template<typename T> +inline T +js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) +{ + MOZ_ASSERT(sizeof(T) < 8 || isLockfree8()); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + T v; + __sync_synchronize(); + do { + v = *addr; + } while (__sync_val_compare_and_swap(addr, v, val) != v); + return v; +# else + T v; + __atomic_exchange(addr, &val, &v, __ATOMIC_SEQ_CST); + return v; +# endif +} + +template<size_t nbytes> +inline void +js::jit::RegionLock::acquire(void* addr) +{ +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + while (!__sync_bool_compare_and_swap(&spinlock, 0, 1)) + ; +# else + uint32_t zero = 0; + uint32_t one = 1; + while (!__atomic_compare_exchange(&spinlock, &zero, &one, false, __ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE)) + continue; +# endif +} + +template<size_t nbytes> +inline void +js::jit::RegionLock::release(void* addr) +{ + MOZ_ASSERT(AtomicOperations::loadSeqCst(&spinlock) == 1, "releasing unlocked region lock"); +# ifdef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + __sync_sub_and_fetch(&spinlock, 1); +# else + uint32_t zero = 0; + __atomic_store(&spinlock, &zero, __ATOMIC_SEQ_CST); +# endif +} + +# undef ATOMICS_IMPLEMENTED_WITH_SYNC_INTRINSICS + +#elif defined(ENABLE_SHARED_ARRAY_BUFFER) + +# error "Either disable JS shared memory, use GCC or Clang, or add code here" + +#endif + +#endif // jit_mips_shared_AtomicOperations_mips_shared_h
deleted file mode 100644 --- a/js/src/jit/mips32/AtomicOperations-mips32.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sts=4 et sw=4 tw=99: - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* For documentation, see jit/AtomicOperations.h */ - -#ifndef jit_mips32_AtomicOperations_mips32_h -#define jit_mips32_AtomicOperations_mips32_h - -#include "jit/AtomicOperations.h" - -inline bool -js::jit::AtomicOperations::isLockfree8() -{ - // Don't crash this one, since it may be read during - // initialization, to cache the value. - return false; -} - -inline void -js::jit::AtomicOperations::fenceSeqCst() -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::loadSeqCst(T* addr) -{ - MOZ_CRASH(); -} - -template<typename T> -inline void -js::jit::AtomicOperations::storeSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::compareExchangeSeqCst(T* addr, T oldval, T newval) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::fetchAddSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::fetchSubSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::fetchAndSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::fetchOrSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::fetchXorSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<typename T> -inline T -js::jit::AtomicOperations::exchangeSeqCst(T* addr, T val) -{ - MOZ_CRASH(); -} - -template<size_t nbytes> -inline void -js::jit::RegionLock::acquire(void* addr) -{ - MOZ_CRASH(); -} - -template<size_t nbytes> -inline void -js::jit::RegionLock::release(void* addr) -{ - MOZ_CRASH(); -} - -#endif // jit_mips32_AtomicOperations_mips32_h
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp +++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp @@ -3453,19 +3453,19 @@ MacroAssembler::callAndPushReturnAddress as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); as_jalr(callee); as_sw(ra, StackPointer, 0); } void MacroAssembler::callAndPushReturnAddress(Label* label) { - // Push return address during jalr delay slot. + // Push return address during bal delay slot. as_addiu(StackPointer, StackPointer, -sizeof(intptr_t)); - as_jalr(label); + ma_bal(label, DontFillDelaySlot); as_sw(ra, StackPointer, 0); } // =============================================================== // ABI function calls. void MacroAssembler::setupUnalignedABICall(Register scratch)
--- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -583,17 +583,17 @@ CodeGeneratorShared::encode(LSnapshot* s } snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId); #endif uint32_t allocIndex = 0; for (LRecoverInfo::OperandIter it(recoverInfo); !it; ++it) { DebugOnly<uint32_t> allocWritten = snapshots_.allocWritten(); encodeAllocation(snapshot, *it, &allocIndex); - MOZ_ASSERT(allocWritten + 1 == snapshots_.allocWritten()); + MOZ_ASSERT_IF(!snapshots_.oom(), allocWritten + 1 == snapshots_.allocWritten()); } MOZ_ASSERT(allocIndex == snapshot->numSlots()); snapshots_.endSnapshot(); snapshot->setSnapshotOffset(offset); masm.propagateOOM(!snapshots_.oom()); }
--- a/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h +++ b/js/src/jit/shared/IonAssemblerBufferWithConstantPools.h @@ -513,17 +513,19 @@ struct AssemblerBufferWithConstantPools inhibitNops_ = false; } } void markNextAsBranch() { // If the previous thing inserted was the last instruction of the node, // then whoops, we want to mark the first instruction of the next node. - this->ensureSpace(InstSize); + if (!this->ensureSpace(InstSize)) + return; + MOZ_ASSERT(this->getTail() != nullptr); this->getTail()->markNextAsBranch(); } bool isNextBranch() const { MOZ_ASSERT(this->getTail() != nullptr); return this->getTail()->isNextBranch(); }
--- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -265,27 +265,31 @@ JSCompartment::checkWrapperMapAfterMovin #endif bool JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped, const js::Value& wrapper) { MOZ_ASSERT(wrapped.wrapped); MOZ_ASSERT_IF(wrapped.kind == CrossCompartmentKey::StringWrapper, wrapper.isString()); MOZ_ASSERT_IF(wrapped.kind != CrossCompartmentKey::StringWrapper, wrapper.isObject()); - bool success = crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper)); /* There's no point allocating wrappers in the nursery since we will tenure them anyway. */ MOZ_ASSERT(!IsInsideNursery(static_cast<gc::Cell*>(wrapper.toGCThing()))); - if (success && (IsInsideNursery(wrapped.wrapped) || IsInsideNursery(wrapped.debugger))) { + if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) { + ReportOutOfMemory(cx); + return false; + } + + if (IsInsideNursery(wrapped.wrapped) || IsInsideNursery(wrapped.debugger)) { WrapperMapRef ref(&crossCompartmentWrappers, wrapped); cx->runtime()->gc.storeBuffer.putGeneric(ref); } - return success; + return true; } static JSString* CopyStringPure(JSContext* cx, JSString* str) { /* * Directly allocate the copy in the destination compartment, rather than * first flattening it (and possibly allocating in source compartment),
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -515,18 +515,20 @@ NewPropertyIteratorObject(JSContext* cx, NativeIterator* NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, const AutoIdVector& props) { JS_STATIC_ASSERT(sizeof(ReceiverGuard) == 2 * sizeof(void*)); size_t plength = props.length(); NativeIterator* ni = cx->zone()->pod_malloc_with_extra<NativeIterator, void*>(plength + numGuards * 2); - if (!ni) + if (!ni) { + ReportOutOfMemory(cx); return nullptr; + } AutoValueVector strings(cx); ni->props_array = ni->props_cursor = reinterpret_cast<HeapPtrFlatString*>(ni + 1); ni->props_end = ni->props_array + plength; if (plength) { for (size_t i = 0; i < plength; i++) { JSFlatString* str = IdToString(cx, props[i]); if (!str || !strings.append(StringValue(str)))
--- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -263,19 +263,19 @@ class BytecodeParser // in the array is the offset of the opcode that defined the // corresponding stack slot. The top of the stack is at position // |stackDepth - 1|. uint32_t* offsetStack; bool captureOffsetStack(LifoAlloc& alloc, const uint32_t* stack, uint32_t depth) { stackDepth = depth; offsetStack = alloc.newArray<uint32_t>(stackDepth); + if (!offsetStack) + return false; if (stackDepth) { - if (!offsetStack) - return false; for (uint32_t n = 0; n < stackDepth; n++) offsetStack[n] = stack[n]; } return true; } // When control-flow merges, intersect the stacks, marking slots that // are defined by different offsets with the UINT32_MAX sentinel.
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3242,18 +3242,20 @@ js::detail::CopyScript(JSContext* cx, Ha uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0; uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0; uint32_t nyieldoffsets = src->hasYieldOffsets() ? src->yieldOffsets().length() : 0; /* Script data */ size_t size = src->dataSize(); uint8_t* data = AllocScriptData(cx->zone(), size); - if (size && !data) + if (size && !data) { + ReportOutOfMemory(cx); return false; + } /* Bindings */ Rooted<Bindings> bindings(cx); if (!Bindings::clone(cx, &bindings, data, src)) return false; /* Objects */
--- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -549,20 +549,22 @@ ObjectGroup::defaultNewGroup(ExclusiveCo } ObjectGroupCompartment::newTablePostBarrier(cx, table, clasp, proto, associated); if (proto.isObject()) { RootedObject obj(cx, proto.toObject()); if (associated) { - if (associated->is<JSFunction>()) - TypeNewScript::make(cx->asJSContext(), group, &associated->as<JSFunction>()); - else + if (associated->is<JSFunction>()) { + if (!TypeNewScript::make(cx->asJSContext(), group, &associated->as<JSFunction>())) + return nullptr; + } else { group->setTypeDescr(&associated->as<TypeDescr>()); + } } /* * Some builtin objects have slotful native properties baked in at * creation via the Shape::{insert,get}initialShape mechanism. Since * these properties are never explicitly defined on new objects, update * the type information for them here. */
--- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -2164,28 +2164,28 @@ class MOZ_STACK_CLASS AutoInitGCManagedO rt->handlingInitFailure = true; ptr_.reset(nullptr); rt->handlingInitFailure = false; } #endif } T& operator*() const { - return *ptr_.get(); + return *get(); } T* operator->() const { - return ptr_.get(); + return get(); } explicit operator bool() const { - return ptr_.get() != nullptr; + return get() != nullptr; } - T* get() { + T* get() const { return ptr_.get(); } T* release() { return ptr_.release(); } AutoInitGCManagedObject(const AutoInitGCManagedObject<T>& other) = delete;
--- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -1105,23 +1105,32 @@ ScopeIter::incrementStaticScopeIter() ssi_++; } void ScopeIter::settle() { // Check for trying to iterate a function frame before the prologue has // created the CallObject, in which case we have to skip. - if (frame_ && frame_.isNonEvalFunctionFrame() && - frame_.fun()->needsCallObject() && !frame_.hasCallObj()) + if (frame_ && frame_.isNonEvalFunctionFrame() && frame_.fun()->needsCallObject() && + !frame_.hasCallObj()) { MOZ_ASSERT(ssi_.type() == StaticScopeIter<CanGC>::Function); incrementStaticScopeIter(); } + // Check for trying to iterate a strict eval frame before the prologue has + // created the CallObject. + if (frame_ && frame_.isStrictEvalFrame() && !frame_.hasCallObj() && !ssi_.done()) { + MOZ_ASSERT(ssi_.type() == StaticScopeIter<CanGC>::Block); + incrementStaticScopeIter(); + MOZ_ASSERT(ssi_.type() == StaticScopeIter<CanGC>::Eval); + incrementStaticScopeIter(); + } + // Check if we have left the extent of the initial frame after we've // settled on a static scope. if (frame_ && (ssi_.done() || maybeStaticScope() == frame_.script()->enclosingStaticScope())) frame_ = NullFramePtr(); #ifdef DEBUG if (!ssi_.done() && hasAnyScopeObject()) { switch (ssi_.type()) {
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -3483,39 +3483,40 @@ PreliminaryObjectArrayWithTemplate::mayb } ///////////////////////////////////////////////////////////////////// // TypeNewScript ///////////////////////////////////////////////////////////////////// // Make a TypeNewScript for |group|, and set it up to hold the preliminary // objects created with the group. -/* static */ void +/* static */ bool TypeNewScript::make(JSContext* cx, ObjectGroup* group, JSFunction* fun) { MOZ_ASSERT(cx->zone()->types.activeAnalysis); MOZ_ASSERT(!group->newScript()); MOZ_ASSERT(!group->maybeUnboxedLayout()); if (group->unknownProperties()) - return; + return true; ScopedJSDeletePtr<TypeNewScript> newScript(cx->new_<TypeNewScript>()); if (!newScript) - return; + return false; newScript->function_ = fun; newScript->preliminaryObjects = group->zone()->new_<PreliminaryObjectArray>(); if (!newScript->preliminaryObjects) - return; + return true; group->setNewScript(newScript.forget()); gc::TraceTypeNewScript(group); + return true; } // Make a TypeNewScript with the same initializer list as |newScript| but with // a new template object. /* static */ TypeNewScript* TypeNewScript::makeNativeVersion(JSContext* cx, TypeNewScript* newScript, PlainObject* templateObject) {
--- a/js/src/vm/TypeInference.h +++ b/js/src/vm/TypeInference.h @@ -959,17 +959,17 @@ class TypeNewScript void trace(JSTracer* trc); void sweep(); void registerNewObject(PlainObject* res); bool maybeAnalyze(JSContext* cx, ObjectGroup* group, bool* regenerate, bool force = false); bool rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* group); - static void make(JSContext* cx, ObjectGroup* group, JSFunction* fun); + static bool make(JSContext* cx, ObjectGroup* group, JSFunction* fun); static TypeNewScript* makeNativeVersion(JSContext* cx, TypeNewScript* newScript, PlainObject* templateObject); size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; /* Is this a reasonable PC to be doing inlining on? */ inline bool isInlinableCall(jsbytecode* pc);
--- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -2038,22 +2038,25 @@ js::TryConvertToUnboxedLayout(ExclusiveC // Accumulate a list of all the values in each preliminary object, and // update their shapes. AutoValueVector values(cx); for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { JSObject* obj = objects->get(i); if (!obj) continue; - if (isArray) { - if (!GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), values)) - return false; - } else { - if (!GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), values)) - return false; + bool ok; + if (isArray) + ok = GetValuesFromPreliminaryArrayObject(&obj->as<ArrayObject>(), values); + else + ok = GetValuesFromPreliminaryPlainObject(&obj->as<PlainObject>(), values); + + if (!ok) { + cx->recoverFromOutOfMemory(); + return false; } } if (TypeNewScript* newScript = group->newScript()) layout->setNewScript(newScript); for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) { if (JSObject* obj = objects->get(i))
new file mode 100644 --- /dev/null +++ b/layout/reftests/font-matching/font-shorthand-stretch-1.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<html> +<head> +<style type="text/css"> +/* load 9 faces of DejaVu Sans in the "dvs" family, with appropriate style descriptors */ +@font-face { + font-family: dvs; + src: url(../fonts/dejavu-sans/DejaVuSans.ttf); +} +@font-face { + font-family: dvs; + font-weight: bold; + src: url(../fonts/dejavu-sans/DejaVuSans-Bold.ttf); +} +@font-face { + font-family: dvs; + font-style: italic; + src: url(../fonts/dejavu-sans/DejaVuSans-Oblique.ttf); +} +@font-face { + font-family: dvs; + font-style: italic; + font-weight: bold; + src: url(../fonts/dejavu-sans/DejaVuSans-BoldOblique.ttf); +} +@font-face { /* note that there is no ExtraLight Condensed or Oblique */ + font-family: dvs; + font-weight: 200; + src: url(../fonts/dejavu-sans/DejaVuSans-ExtraLight.ttf); +} +@font-face { + font-family: dvs; + font-stretch: condensed; + src: url(../fonts/dejavu-sans/DejaVuSansCondensed.ttf); +} +@font-face { + font-family: dvs; + font-weight: bold; + font-stretch: condensed; + src: url(../fonts/dejavu-sans/DejaVuSansCondensed-Bold.ttf); +} +@font-face { + font-family: dvs; + font-style: italic; + font-stretch: condensed; + src: url(../fonts/dejavu-sans/DejaVuSansCondensed-Oblique.ttf); +} +@font-face { + font-family: dvs; + font-style: italic; + font-weight: bold; + font-stretch: condensed; + src: url(../fonts/dejavu-sans/DejaVuSansCondensed-BoldOblique.ttf); +} + +body { + font-family: dvs, serif; + font-size: 24px; +} +.l { + font-weight: 200; +} +</style> +</head> +<body> +<!-- all 4 levels of "condensed" come out the same; "condensed" takes priority over "light" --> +<div style="font: ultra-condensed 24px dvs, serif">ultra-condensed <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: extra-condensed 24px dvs, serif">extra-condensed <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: condensed 24px dvs, serif">condensed <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: semi-condensed 24px dvs, serif">semi-condensed <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<!-- "normal" and all 4 levels of "expanded" come out the same; "light" is available, but only in upright, not italic --> +<div style="font: 24px dvs">normal <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: semi-expanded 24px dvs, serif">semi-expanded <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: expanded 24px dvs, serif">expanded <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: extra-expanded 24px dvs, serif">extra-expanded <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +<div style="font: ultra-expanded 24px dvs, serif">ultra-expanded <i>italic</i> <b>bold <i>italic</i></b> <span class="l">light <i>italic</i></span></div> +</body> +</html> +
--- a/layout/reftests/font-matching/reftest.list +++ b/layout/reftests/font-matching/reftest.list @@ -67,16 +67,17 @@ HTTP(..) == weightmapping-12579.html wei skip-if(B2G||Mulet) HTTP(..) == stretchmapping-all.html stretchmapping-all-ref.html # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop skip-if(B2G||Mulet) HTTP(..) == stretchmapping-reverse.html stretchmapping-reverse-ref.html # Initial mulet triage: parity with B2G/B2G Desktop fuzzy-if(Android,4,8) HTTP(..) == stretchmapping-35.html stretchmapping-35-ref.html HTTP(..) == stretchmapping-137.html stretchmapping-137-ref.html # test for font-stretch using @font-face skip-if(B2G||Mulet) skip-if(Android&&AndroidVersion>15) HTTP(..) == font-stretch-1.html font-stretch-1-ref.html # bugs 773482, 927602 # Initial mulet triage: parity with B2G/B2G Desktop +skip-if(B2G||Mulet) skip-if(Android&&AndroidVersion>15) HTTP(..) == font-shorthand-stretch-1.html font-stretch-1-ref.html # bugs 773482, 927602 # Initial mulet triage: parity with B2G/B2G Desktop # bug 724231 - applying synthetic styles to a single @font-face font # should apply artificial obliquing, not switch to a true styled face != synthetic-style-1.html synthetic-style-1-notref.html != synthetic-style-2.html synthetic-style-2-notref.html # Bug 765906 - synthetic bold should be used if necessary together with system fallback. # **NOTE** we skip these on Linux because of bug 769659.
--- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -578,19 +578,17 @@ Declaration::GetValue(nsCSSProperty aPro // This can't be represented as a shorthand. return; } systemFont->AppendToString(eCSSProperty__x_system_font, aValue, aSerialization); } else { // properties reset by this shorthand property to their // initial values but not represented in its syntax - if (stretch->GetUnit() != eCSSUnit_Enumerated || - stretch->GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL || - sizeAdjust->GetUnit() != eCSSUnit_None || + if (sizeAdjust->GetUnit() != eCSSUnit_None || featureSettings->GetUnit() != eCSSUnit_Normal || languageOverride->GetUnit() != eCSSUnit_Normal || fontKerning->GetIntValue() != NS_FONT_KERNING_AUTO || fontSynthesis->GetUnit() != eCSSUnit_Enumerated || fontSynthesis->GetIntValue() != (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) || fontVariantAlternates->GetUnit() != eCSSUnit_Normal || fontVariantEastAsian->GetUnit() != eCSSUnit_Normal || @@ -620,16 +618,22 @@ Declaration::GetValue(nsCSSProperty aPro aValue.Append(char16_t(' ')); } if (weight->GetUnit() != eCSSUnit_Enumerated || weight->GetIntValue() != NS_FONT_WEIGHT_NORMAL) { weight->AppendToString(eCSSProperty_font_weight, aValue, aSerialization); aValue.Append(char16_t(' ')); } + if (stretch->GetUnit() != eCSSUnit_Enumerated || + stretch->GetIntValue() != NS_FONT_STRETCH_NORMAL) { + stretch->AppendToString(eCSSProperty_font_stretch, aValue, + aSerialization); + aValue.Append(char16_t(' ')); + } size->AppendToString(eCSSProperty_font_size, aValue, aSerialization); if (lh->GetUnit() != eCSSUnit_Normal) { aValue.Append(char16_t('/')); lh->AppendToString(eCSSProperty_line_height, aValue, aSerialization); } aValue.Append(char16_t(' ')); family->AppendToString(eCSSProperty_font_family, aValue, aSerialization);
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -12165,22 +12165,16 @@ CSSParserImpl::ParseCursor() AppendValue(eCSSProperty_cursor, value); return true; } bool CSSParserImpl::ParseFont() { - static const nsCSSProperty fontIDs[] = { - eCSSProperty_font_style, - eCSSProperty_font_variant_caps, - eCSSProperty_font_weight - }; - nsCSSValue family; if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) { if (eCSSUnit_Inherit == family.GetUnit() || eCSSUnit_Initial == family.GetUnit() || eCSSUnit_Unset == family.GetUnit()) { AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None)); AppendValue(eCSSProperty_font_family, family); AppendValue(eCSSProperty_font_style, family); @@ -12219,45 +12213,70 @@ CSSParserImpl::ParseFont() AppendValue(eCSSProperty_font_variant_east_asian, systemFont); AppendValue(eCSSProperty_font_variant_ligatures, systemFont); AppendValue(eCSSProperty_font_variant_numeric, systemFont); AppendValue(eCSSProperty_font_variant_position, systemFont); } return true; } - // Get optional font-style, font-variant and font-weight (in any order) - const int32_t numProps = 3; + // Get optional font-style, font-variant, font-weight, font-stretch + // (in any order) + + // Indexes into fontIDs[] and values[] arrays. + const int kFontStyleIndex = 0; + const int kFontVariantIndex = 1; + const int kFontWeightIndex = 2; + const int kFontStretchIndex = 3; + + // The order of the initializers here must match the order of the indexes + // defined above! + static const nsCSSProperty fontIDs[] = { + eCSSProperty_font_style, + eCSSProperty_font_variant_caps, + eCSSProperty_font_weight, + eCSSProperty_font_stretch + }; + + const int32_t numProps = MOZ_ARRAY_LENGTH(fontIDs); nsCSSValue values[numProps]; int32_t found = ParseChoice(values, fontIDs, numProps); if (found < 0 || - eCSSUnit_Inherit == values[0].GetUnit() || - eCSSUnit_Initial == values[0].GetUnit() || - eCSSUnit_Unset == values[0].GetUnit()) { // illegal data - return false; - } - if ((found & 1) == 0) { + eCSSUnit_Inherit == values[kFontStyleIndex].GetUnit() || + eCSSUnit_Initial == values[kFontStyleIndex].GetUnit() || + eCSSUnit_Unset == values[kFontStyleIndex].GetUnit()) { // illegal data + return false; + } + if ((found & (1 << kFontStyleIndex)) == 0) { // Provide default font-style - values[0].SetIntValue(NS_FONT_STYLE_NORMAL, eCSSUnit_Enumerated); - } - if ((found & 2) == 0) { + values[kFontStyleIndex].SetIntValue(NS_FONT_STYLE_NORMAL, + eCSSUnit_Enumerated); + } + if ((found & (1 << kFontVariantIndex)) == 0) { // Provide default font-variant - values[1].SetNormalValue(); + values[kFontVariantIndex].SetNormalValue(); } else { - if (values[1].GetUnit() == eCSSUnit_Enumerated && - values[1].GetIntValue() != NS_FONT_VARIANT_CAPS_SMALLCAPS) { + if (values[kFontVariantIndex].GetUnit() == eCSSUnit_Enumerated && + values[kFontVariantIndex].GetIntValue() != + NS_FONT_VARIANT_CAPS_SMALLCAPS) { // only normal or small-caps is allowed in font shorthand // this also assumes other values for font-variant-caps never overlap // possible values for style or weight return false; } } - if ((found & 4) == 0) { + if ((found & (1 << kFontWeightIndex)) == 0) { // Provide default font-weight - values[2].SetIntValue(NS_FONT_WEIGHT_NORMAL, eCSSUnit_Enumerated); + values[kFontWeightIndex].SetIntValue(NS_FONT_WEIGHT_NORMAL, + eCSSUnit_Enumerated); + } + if ((found & (1 << kFontStretchIndex)) == 0) { + // Provide default font-stretch + values[kFontStretchIndex].SetIntValue(NS_FONT_STRETCH_NORMAL, + eCSSUnit_Enumerated); } // Get mandatory font-size nsCSSValue size; if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) { return false; } @@ -12278,23 +12297,22 @@ CSSParserImpl::ParseFont() // Get final mandatory font-family nsAutoParseCompoundProperty compound(this); if (ParseFamily(family)) { if (eCSSUnit_Inherit != family.GetUnit() && eCSSUnit_Initial != family.GetUnit() && eCSSUnit_Unset != family.GetUnit()) { AppendValue(eCSSProperty__x_system_font, nsCSSValue(eCSSUnit_None)); AppendValue(eCSSProperty_font_family, family); - AppendValue(eCSSProperty_font_style, values[0]); - AppendValue(eCSSProperty_font_variant_caps, values[1]); - AppendValue(eCSSProperty_font_weight, values[2]); + AppendValue(eCSSProperty_font_style, values[kFontStyleIndex]); + AppendValue(eCSSProperty_font_variant_caps, values[kFontVariantIndex]); + AppendValue(eCSSProperty_font_weight, values[kFontWeightIndex]); AppendValue(eCSSProperty_font_size, size); AppendValue(eCSSProperty_line_height, lineHeight); - AppendValue(eCSSProperty_font_stretch, - nsCSSValue(NS_FONT_STRETCH_NORMAL, eCSSUnit_Enumerated)); + AppendValue(eCSSProperty_font_stretch, values[kFontStretchIndex]); AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None)); AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal)); AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal)); AppendValue(eCSSProperty_font_kerning, nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated)); AppendValue(eCSSProperty_font_synthesis, nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE, eCSSUnit_Enumerated));
--- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -2485,17 +2485,17 @@ var gCSSProperties = { domProp: "font", inherited: true, type: CSS_TYPE_TRUE_SHORTHAND, subproperties: [ "font-style", "font-variant", "font-weight", "font-size", "line-height", "font-family", "font-stretch", "font-size-adjust", "font-feature-settings", "font-language-override", "font-kerning", "font-synthesis", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position" ], initial_values: [ (gInitialFontFamilyIsSansSerif ? "medium sans-serif" : "medium serif") ], - other_values: [ "large serif", "9px fantasy", "bold italic small-caps 24px/1.4 Times New Roman, serif", "small inherit roman", "small roman inherit", + other_values: [ "large serif", "9px fantasy", "condensed bold italic small-caps 24px/1.4 Times New Roman, serif", "small inherit roman", "small roman inherit", // system fonts "caption", "icon", "menu", "message-box", "small-caption", "status-bar", // Gecko-specific system fonts "-moz-window", "-moz-document", "-moz-desktop", "-moz-info", "-moz-dialog", "-moz-button", "-moz-pull-down-menu", "-moz-list", "-moz-field", "-moz-workspace", ], invalid_values: [ "9 fantasy", "-2px fantasy" ] }, "font-family": {
--- a/layout/style/test/test_bug377947.html +++ b/layout/style/test/test_bug377947.html @@ -51,18 +51,18 @@ is(s.getPropertyValue("list-style"), "", is(s.getPropertyValue("font"), "", "font shorthand should start off empty"); var all_but_one = { "font-family": "serif", "font-style": "normal", "font-variant": "normal", "font-weight": "bold", "font-size": "small", + "font-stretch": "normal", "font-size-adjust": "none", // has to be default value - "font-stretch": "normal", // has to be default value "font-feature-settings": "normal", // has to be default value "font-language-override": "normal", // has to be default value "font-kerning": "auto", // has to be default value "font-synthesis": "weight style", // has to be default value "font-variant-alternates": "normal", // has to be default value "font-variant-caps": "normal", // has to be default value "font-variant-east-asian": "normal", // has to be default value "font-variant-ligatures": "normal", // has to be default value @@ -73,20 +73,20 @@ var all_but_one = { for (var prop in all_but_one) { s.setProperty(prop, all_but_one[prop], ""); } is(s.getPropertyValue("font"), "", "font shorthand should be empty when some subproperties specified"); s.setProperty("line-height", "1.5", ""); isnot(s.getPropertyValue("font"), "", "font shorthand should produce value when all subproperties set"); -s.setProperty("font-stretch", "condensed", ""); +s.setProperty("font-size-adjust", "0.5", ""); is(s.getPropertyValue("font"), "", - "font shorthand should be empty when font-stretch is non-default"); -s.setProperty("font-stretch", "normal", ""); + "font shorthand should be empty when font-size-adjust is non-default"); +s.setProperty("font-size-adjust", "none", ""); isnot(s.getPropertyValue("font"), "", "font shorthand should produce value when all subproperties set"); s.removeProperty("font"); is(s.getPropertyValue("font"), "", "font shorthand be empty after removal"); s.font="medium serif"; isnot(s.getPropertyValue("font"), "", "font shorthand should produce value when shorthand set");
--- a/layout/style/test/test_shorthand_property_getters.html +++ b/layout/style/test/test_shorthand_property_getters.html @@ -108,17 +108,17 @@ is(e.style.cssText, "border-radius: 1px // Test that we refuse to serialize the 'background' and 'font' // shorthands when some subproperties that can't be expressed in the // shorthand syntax are present. e.setAttribute("style", "font: medium serif"); isnot(e.style.font, "", "should have font shorthand"); e.setAttribute("style", "font: medium serif; font-size-adjust: 0.45"); is(e.style.font, "", "should not have font shorthand"); -e.setAttribute("style", "font: medium serif; font-stretch: condensed"); +e.setAttribute("style", "font: medium serif; font-feature-settings: 'liga' off"); is(e.style.font, "", "should not have font shorthand"); // Test that all combinations of background-clip and background-origin // can be expressed in the shorthand (which wasn't the case previously). e.setAttribute("style", "background: red"); isnot(e.style.background, "", "should have background shorthand"); e.setAttribute("style", "background: red; background-origin: border-box"); isnot(e.style.background, "", "should have background shorthand (origin:border-box)");
--- a/layout/tools/reftest/remotereftest.py +++ b/layout/tools/reftest/remotereftest.py @@ -354,18 +354,20 @@ class RemoteReftest(RefTest): prefs["reftest.remote"] = True # Set a future policy version to avoid the telemetry prompt. prefs["toolkit.telemetry.prompted"] = 999 prefs["toolkit.telemetry.notifiedOptOut"] = 999 prefs["reftest.uri"] = "%s" % reftestlist prefs["datareporting.policy.dataSubmissionPolicyBypassAcceptance"] = True # Point the url-classifier to the local testing server for fast failures - prefs["browser.safebrowsing.gethashURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/gethash" - prefs["browser.safebrowsing.updateURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/update" + prefs["browser.safebrowsing.provider.google.gethashURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/gethash" + prefs["browser.safebrowsing.provider.google.updateURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/update" + prefs["browser.safebrowsing.provider.mozilla.gethashURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/gethash" + prefs["browser.safebrowsing.provider.mozilla.updateURL"] = "http://127.0.0.1:8888/safebrowsing-dummy/update" # Point update checks to the local testing server for fast failures prefs["extensions.update.url"] = "http://127.0.0.1:8888/extensions-dummy/updateURL" prefs["extensions.update.background.url"] = "http://127.0.0.1:8888/extensions-dummy/updateBackgroundURL" prefs["extensions.blocklist.url"] = "http://127.0.0.1:8888/extensions-dummy/blocklistURL" prefs["extensions.hotfix.url"] = "http://127.0.0.1:8888/extensions-dummy/hotfixURL" # Turn off extension updates so they don't bother tests prefs["extensions.update.enabled"] = False # Make sure opening about:addons won't hit the network
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -850,16 +850,19 @@ status_t MPEG4Extractor::parseChunk(off6 sp<MPEG4DataSource> cachedSource = new MPEG4DataSource(mDataSource); if (cachedSource->setCachedRange(*offset, chunk_size) == OK) { mDataSource = cachedSource; } } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->sampleTable = new SampleTable(mDataSource); } bool isTrack = false; if (chunk_type == FOURCC('t', 'r', 'a', 'k')) { isTrack = true; Track *track = new Track; @@ -975,39 +978,48 @@ status_t MPEG4Extractor::parseChunk(off6 return ERROR_IO; } entriesoffset += 4; // ignore media_rate_integer and media_rate_fraction. if (media_time == -1 && i) { ALOGW("ignoring invalid empty edit", i); break; } else if (media_time == -1) { // Starting offsets for tracks (streams) are represented by an initial empty edit. + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->empty_duration = segment_duration; continue; } else if (i > 1) { // we only support a single non-empty entry at the moment, for gapless playback ALOGW("multiple edit list entries, A/V sync will be wrong"); break; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->segment_duration = segment_duration; mLastTrack->media_time = media_time; } storeEditList(); *offset += chunk_size; break; } case FOURCC('f', 'r', 'm', 'a'): { uint32_t original_fourcc; if (mDataSource->readAt(data_offset, &original_fourcc, 4) < 4) { return ERROR_IO; } original_fourcc = ntohl(original_fourcc); ALOGV("read original format: %d", original_fourcc); + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc)); uint32_t num_channels = 0; uint32_t sample_rate = 0; if (AdjustChannelsAndRate(original_fourcc, &num_channels, &sample_rate)) { mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); mLastTrack->meta->setInt32(kKeySampleRate, sample_rate); } *offset += chunk_size; @@ -1062,16 +1074,19 @@ status_t MPEG4Extractor::parseChunk(off6 } uint8_t defaultKeyId[16]; if (mDataSource->readAt(data_offset + 8, &defaultKeyId, 16) < 16) { return ERROR_IO; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId); mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize); mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16); *offset += chunk_size; break; } case FOURCC('t', 'r', 'e', 'x'): @@ -1151,16 +1166,19 @@ status_t MPEG4Extractor::parseChunk(off6 uint32_t timescale; if (mDataSource->readAt( timescale_offset, ×cale, sizeof(timescale)) < (ssize_t)sizeof(timescale)) { return ERROR_IO; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->timescale = ntohl(timescale); // Now that we've parsed the media timescale, we can interpret // the edit list data. storeEditList(); int64_t duration = 0; if (version == 1) { @@ -1243,16 +1261,19 @@ status_t MPEG4Extractor::parseChunk(off6 uint32_t entry_count = U32_AT(&buffer[4]); if (entry_count > 1) { // For 3GPP timed text, there could be multiple tx3g boxes contain // multiple text display formats. These formats will be used to // display the timed text. // For encrypted files, there may also be more than one entry. const char *mime; + if (!mLastTrack) { + return ERROR_MALFORMED; + } CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) && strcasecmp(mime, "application/octet-stream")) { // For now we only support a single type of media per track. mLastTrack->skipTrack = true; *offset += chunk_size; break; } @@ -1297,16 +1318,19 @@ status_t MPEG4Extractor::parseChunk(off6 uint16_t data_ref_index = U16_AT(&buffer[6]); uint16_t qt_version = U16_AT(&buffer[8]); uint32_t num_channels = U16_AT(&buffer[16]); uint16_t sample_size = U16_AT(&buffer[18]); uint32_t sample_rate = U32_AT(&buffer[24]) >> 16; + if (!mLastTrack) { + return ERROR_MALFORMED; + } if (chunk_type != FOURCC('e', 'n', 'c', 'a')) { // if the chunk type is enca, we'll get the type from the sinf/frma box later mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate); } ALOGV("*** coding='%s' %d channels, size %d, rate %d\n", chunk, num_channels, sample_size, sample_rate); mLastTrack->meta->setInt32(kKeyChannelCount, num_channels); @@ -1392,16 +1416,19 @@ status_t MPEG4Extractor::parseChunk(off6 // let the decoder figure out the actual width and height (and thus // be prepared for INFO_FOMRAT_CHANGED event). if (width == 0) width = 352; if (height == 0) height = 288; // printf("*** coding='%s' width=%d height=%d\n", // chunk, width, height); + if (!mLastTrack) { + return ERROR_MALFORMED; + } if (chunk_type != FOURCC('e', 'n', 'c', 'v')) { // if the chunk type is encv, we'll get the type from the sinf/frma box later mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); } mLastTrack->meta->setInt32(kKeyWidth, width); mLastTrack->meta->setInt32(kKeyHeight, height); off64_t stop_offset = *offset + chunk_size; @@ -1422,45 +1449,54 @@ status_t MPEG4Extractor::parseChunk(off6 return ERROR_MALFORMED; } break; } case FOURCC('s', 't', 'c', 'o'): case FOURCC('c', 'o', '6', '4'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setChunkOffsetParams( chunk_type, data_offset, chunk_data_size); if (err != OK) { return err; } *offset += chunk_size; break; } case FOURCC('s', 't', 's', 'c'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setSampleToChunkParams( data_offset, chunk_data_size); if (err != OK) { return err; } *offset += chunk_size; break; } case FOURCC('s', 't', 's', 'z'): case FOURCC('s', 't', 'z', '2'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setSampleSizeParams( chunk_type, data_offset, chunk_data_size); if (err != OK) { return err; } @@ -1505,72 +1541,87 @@ status_t MPEG4Extractor::parseChunk(off6 } } break; } case FOURCC('s', 't', 't', 's'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setTimeToSampleParams( data_offset, chunk_data_size); if (err != OK) { return err; } *offset += chunk_size; break; } case FOURCC('c', 't', 't', 's'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setCompositionTimeToSampleParams( data_offset, chunk_data_size); if (err != OK) { return err; } *offset += chunk_size; break; } case FOURCC('s', 't', 's', 's'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setSyncSampleParams( data_offset, chunk_data_size); if (err != OK) { return err; } *offset += chunk_size; break; } case FOURCC('s', 'a', 'i', 'z'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setSampleAuxiliaryInformationSizeParams( data_offset, chunk_data_size, mDrmScheme); if (err != OK) { return err; } *offset += chunk_size; break; } case FOURCC('s', 'a', 'i', 'o'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } status_t err = mLastTrack->sampleTable->setSampleAuxiliaryInformationOffsetParams( data_offset, chunk_data_size, mDrmScheme); if (err != OK) { return err; } @@ -1629,16 +1680,19 @@ status_t MPEG4Extractor::parseChunk(off6 return ERROR_IO; } if (U32_AT(buffer) != 0) { // Should be version 0, flags 0. return ERROR_MALFORMED; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setData( kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4); if (mPath.size() >= 2 && (mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a') || (mPath[mPath.size() - 2] == FOURCC('e', 'n', 'c', 'a')))) { // Information from the ESDS must be relied on for proper // setup of sample rate and channel count for MPEG4 Audio. @@ -1661,16 +1715,19 @@ status_t MPEG4Extractor::parseChunk(off6 { sp<ABuffer> buffer = new ABuffer(chunk_data_size); if (mDataSource->readAt( data_offset, buffer->data(), chunk_data_size) < chunk_data_size) { return ERROR_IO; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setData( kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size); *offset += chunk_size; break; } case FOURCC('d', '2', '6', '3'): @@ -1693,16 +1750,19 @@ status_t MPEG4Extractor::parseChunk(off6 return ERROR_MALFORMED; } if (mDataSource->readAt( data_offset, buffer, chunk_data_size) < chunk_data_size) { return ERROR_IO; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size); *offset += chunk_size; break; } case FOURCC('m', 'e', 't', 'a'): { @@ -1855,25 +1915,31 @@ status_t MPEG4Extractor::parseChunk(off6 return ERROR_IO; } uint32_t type = ntohl(buffer); // For the 3GPP file format, the handler-type within the 'hdlr' box // shall be 'text'. We also want to support 'sbtl' handler type // for a practical reason as various MPEG4 containers use it. if (type == FOURCC('t', 'e', 'x', 't') || type == FOURCC('s', 'b', 't', 'l')) { + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP); } *offset += chunk_size; break; } case FOURCC('t', 'x', '3', 'g'): { + if (!mLastTrack) { + return ERROR_MALFORMED; + } uint32_t type; const void *data; size_t size = 0; if (!mLastTrack->meta->findData( kKeyTextFormatData, &type, &data, &size)) { size = 0; } @@ -1965,16 +2031,17 @@ status_t MPEG4Extractor::parseChunk(off6 } return OK; } void MPEG4Extractor::storeEditList() { if (mHeaderTimescale == 0 || + !mLastTrack || mLastTrack->timescale == 0) { return; } uint64_t segment_duration = (mLastTrack->segment_duration * 1000000) / mHeaderTimescale; // media_time is measured in media time scale units. int64_t media_time = (mLastTrack->media_time * 1000000) / mLastTrack->timescale; // empty_duration is in the Movie Header Box's timescale. @@ -2104,16 +2171,19 @@ status_t MPEG4Extractor::parseSegmentInd se.mSize = d1 & 0x7fffffff; se.mDurationUs = 1000000LL * d2 / timeScale; mSidxEntries.add(se); } mSidxDuration = total_duration * 1000000 / timeScale; ALOGV("duration: %lld", mSidxDuration); + if (!mLastTrack) { + return ERROR_MALFORMED; + } int64_t metaDuration; if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) { mLastTrack->meta->setInt64(kKeyDuration, mSidxDuration); } return OK; } status_t MPEG4Extractor::parseTrackExtends( @@ -2173,16 +2243,19 @@ status_t MPEG4Extractor::parseTrackHeade ctime = U32_AT(&buffer[4]); mtime = U32_AT(&buffer[8]); id = U32_AT(&buffer[12]); duration = U32_AT(&buffer[20]); } else { return ERROR_UNSUPPORTED; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setInt32(kKeyTrackID, id); size_t matrixOffset = dynSize + 16; int32_t a00 = U32_AT(&buffer[matrixOffset]); int32_t a01 = U32_AT(&buffer[matrixOffset + 4]); int32_t dx = U32_AT(&buffer[matrixOffset + 8]); int32_t a10 = U32_AT(&buffer[matrixOffset + 12]); int32_t a11 = U32_AT(&buffer[matrixOffset + 16]); @@ -2354,16 +2427,19 @@ status_t MPEG4Extractor::parseMetaData(o (mLastCommentName.length() != 0) && (mLastCommentData.length() != 0)) { if (mLastCommentMean == "com.apple.iTunes" && mLastCommentName == "iTunSMPB") { int32_t delay, padding; if (sscanf(mLastCommentData, " %*x %x %x %*x", &delay, &padding) == 2) { + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); mLastTrack->meta->setInt32(kKeyEncoderPadding, padding); } } mLastCommentMean.clear(); mLastCommentName.clear(); mLastCommentData.clear(); @@ -2475,22 +2551,28 @@ status_t MPEG4Extractor::updateAudioTrac uint8_t objectTypeIndication; if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) { return ERROR_MALFORMED; } if (objectTypeIndication == 0xe1) { // This isn't MPEG4 audio at all, it's QCELP 14k... + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP); return OK; } if (objectTypeIndication == 0x6b || objectTypeIndication == 0x69) { // The media subtype is MP3 audio + if (!mLastTrack) { + return ERROR_MALFORMED; + } mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); } const uint8_t *csd; size_t csd_size; if (esds.getCodecSpecificInfo( (const void **)&csd, &csd_size) != OK) { return ERROR_MALFORMED; @@ -2516,17 +2598,20 @@ status_t MPEG4Extractor::updateAudioTrac ABitReader br(csd, csd_size); uint32_t objectType = br.getBits(5); if (objectType == 31) { // AAC-ELD => additional 6 bits objectType = 32 + br.getBits(6); } if (objectType >= 1 && objectType <= 4) { - mLastTrack->meta->setInt32(kKeyAACProfile, objectType); + if (!mLastTrack) { + return ERROR_MALFORMED; + } + mLastTrack->meta->setInt32(kKeyAACProfile, objectType); } uint32_t freqIndex = br.getBits(4); int32_t sampleRate = 0; int32_t numChannels = 0; if (freqIndex == 15) { if (csd_size < 5) { @@ -2560,16 +2645,19 @@ status_t MPEG4Extractor::updateAudioTrac sampleRate = kSamplingRate[freqIndex]; } } if (numChannels == 0) { return ERROR_UNSUPPORTED; } + if (!mLastTrack) { + return ERROR_MALFORMED; + } int32_t prevSampleRate; CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate)); if (prevSampleRate != sampleRate) { ALOGV("mpeg4 audio sample rate different from previous setting. " "was: %d, now: %d", prevSampleRate, sampleRate); }
--- a/media/libstagefright/gtest/TestParser.cpp +++ b/media/libstagefright/gtest/TestParser.cpp @@ -1,15 +1,16 @@ /* -*- 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 "gtest/gtest.h" #include "MediaData.h" +#include "mozilla/ArrayUtils.h" #include "mp4_demuxer/BufferStream.h" #include "mp4_demuxer/MP4Metadata.h" #include "mp4_demuxer/MoofParser.h" using namespace mozilla; using namespace mp4_demuxer; TEST(stagefright_MP4Metadata, EmptyStream) @@ -37,18 +38,18 @@ TEST(stagefright_MP4Metadata, EmptyStrea EXPECT_FALSE(metadata.Crypto().valid); } TEST(stagefright_MoofParser, EmptyStream) { nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(0); nsRefPtr<BufferStream> stream = new BufferStream(buffer); - mozilla::Monitor monitor("MP4Metadata::gtest"); - mozilla::MonitorAutoLock mon(monitor); + Monitor monitor("MP4Metadata::gtest"); + MonitorAutoLock mon(monitor); MoofParser parser(stream, 0, false, &monitor); EXPECT_EQ(0u, parser.mOffset); EXPECT_TRUE(parser.ReachedEnd()); nsTArray<MediaByteRange> byteRanges; byteRanges.AppendElement(MediaByteRange(0, 0)); EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges)); @@ -57,8 +58,349 @@ TEST(stagefright_MoofParser, EmptyStream EXPECT_EQ(0u, parser.mOffset); EXPECT_TRUE(parser.ReachedEnd()); EXPECT_FALSE(parser.HasMetadata()); nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata(); EXPECT_FALSE(metadataBuffer); EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsNull()); EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsNull()); } + +uint8_t test_case_mp4[] = { + 0x00, 0x00, 0x00, 0x20, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm', + 0x00, 0x00, 0x02, 0x00, 'i', 's', 'o', 'm', 'i', 's', 'o', '2', + 'a', 'v', 'c', '1', 'm', 'p', '4', '1', 0x00, 0x00, 0x00, 0x08, + 'f', 'r', 'e', 'e', 0x00, 0x00, 0x07, 0xcc, 'm', 'd', 'a', 't', + 0x00, 0x00, 0x02, 0xaf, 0x06, 0x05, 0xff, 0xff, 0xab, 0xdc, 0x45, 0xe9, + 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, + 0xef, 0x78, 0x32, 0x36, 0x34, 0x20, 0x2d, 0x20, 0x63, 0x6f, 0x72, 0x65, + 0x20, 0x31, 0x34, 0x36, 0x20, 0x72, 0x32, 0x35, 0x33, 0x38, 0x20, 0x31, + 0x32, 0x31, 0x33, 0x39, 0x36, 0x63, 0x20, 0x2d, 0x20, 0x48, 0x2e, 0x32, + 0x36, 0x34, 0x2f, 0x4d, 0x50, 0x45, 0x47, 0x2d, 0x34, 0x20, 0x41, 0x56, + 0x43, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x20, 0x2d, 0x20, 0x43, 0x6f, + 0x70, 0x79, 0x6c, 0x65, 0x66, 0x74, 0x20, 0x32, 0x30, 0x30, 0x33, 0x2d, + 0x32, 0x30, 0x31, 0x35, 0x20, 0x2d, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x6c, + 0x61, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x78, 0x32, 0x36, 0x34, 0x2e, + 0x68, 0x74, 0x6d, 0x6c, 0x20, 0x2d, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x3a, 0x20, 0x63, 0x61, 0x62, 0x61, 0x63, 0x3d, 0x31, 0x20, + 0x72, 0x65, 0x66, 0x3d, 0x33, 0x20, 0x64, 0x65, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x3d, 0x31, 0x3a, 0x30, 0x3a, 0x30, 0x20, 0x61, 0x6e, 0x61, 0x6c, + 0x79, 0x73, 0x65, 0x3d, 0x30, 0x78, 0x33, 0x3a, 0x30, 0x78, 0x31, 0x31, + 0x33, 0x20, 0x6d, 0x65, 0x3d, 0x68, 0x65, 0x78, 0x20, 0x73, 0x75, 0x62, + 0x6d, 0x65, 0x3d, 0x37, 0x20, 0x70, 0x73, 0x79, 0x3d, 0x31, 0x20, 0x70, + 0x73, 0x79, 0x5f, 0x72, 0x64, 0x3d, 0x31, 0x2e, 0x30, 0x30, 0x3a, 0x30, + 0x2e, 0x30, 0x30, 0x20, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x72, 0x65, + 0x66, 0x3d, 0x31, 0x20, 0x6d, 0x65, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, + 0x3d, 0x31, 0x36, 0x20, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x61, 0x5f, 0x6d, + 0x65, 0x3d, 0x31, 0x20, 0x74, 0x72, 0x65, 0x6c, 0x6c, 0x69, 0x73, 0x3d, + 0x31, 0x20, 0x38, 0x78, 0x38, 0x64, 0x63, 0x74, 0x3d, 0x31, 0x20, 0x63, + 0x71, 0x6d, 0x3d, 0x30, 0x20, 0x64, 0x65, 0x61, 0x64, 0x7a, 0x6f, 0x6e, + 0x65, 0x3d, 0x32, 0x31, 0x2c, 0x31, 0x31, 0x20, 0x66, 0x61, 0x73, 0x74, + 0x5f, 0x70, 0x73, 0x6b, 0x69, 0x70, 0x3d, 0x31, 0x20, 0x63, 0x68, 0x72, + 0x6f, 0x6d, 0x61, 0x5f, 0x71, 0x70, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x3d, 0x2d, 0x32, 0x20, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, + 0x3d, 0x31, 0x32, 0x20, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, + 0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x3d, 0x31, 0x20, + 0x73, 0x6c, 0x69, 0x63, 0x65, 0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x73, 0x3d, 0x30, 0x20, 0x6e, 0x72, 0x3d, 0x30, 0x20, 0x64, 0x65, + 0x63, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x3d, 0x31, 0x20, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6c, 0x61, 0x63, 0x65, 0x64, 0x3d, 0x30, 0x20, 0x62, 0x6c, + 0x75, 0x72, 0x61, 0x79, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x3d, + 0x30, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x65, + 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x61, 0x3d, 0x30, 0x20, 0x62, 0x66, + 0x72, 0x61, 0x6d, 0x65, 0x73, 0x3d, 0x33, 0x20, 0x62, 0x5f, 0x70, 0x79, + 0x72, 0x61, 0x6d, 0x69, 0x64, 0x3d, 0x32, 0x20, 0x62, 0x5f, 0x61, 0x64, + 0x61, 0x70, 0x74, 0x3d, 0x31, 0x20, 0x62, 0x5f, 0x62, 0x69, 0x61, 0x73, + 0x3d, 0x30, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x3d, 0x31, 0x20, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x62, 0x3d, 0x31, 0x20, 0x6f, 0x70, + 0x65, 0x6e, 0x5f, 0x67, 0x6f, 0x70, 0x3d, 0x30, 0x20, 0x77, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x70, 0x3d, 0x32, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, + 0x74, 0x3d, 0x32, 0x35, 0x30, 0x20, 0x6b, 0x65, 0x79, 0x69, 0x6e, 0x74, + 0x5f, 0x6d, 0x69, 0x6e, 0x3d, 0x32, 0x35, 0x20, 0x73, 0x63, 0x65, 0x6e, + 0x65, 0x63, 0x75, 0x74, 0x3d, 0x34, 0x30, 0x20, 0x69, 0x6e, 0x74, 0x72, + 0x61, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x3d, 0x30, 0x20, + 0x72, 0x63, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x61, 0x68, 0x65, 0x61, 0x64, + 0x3d, 0x34, 0x30, 0x20, 0x72, 0x63, 0x3d, 0x63, 0x72, 0x66, 0x20, 0x6d, + 0x62, 0x74, 0x72, 0x65, 0x65, 0x3d, 0x31, 0x20, 0x63, 0x72, 0x66, 0x3d, + 0x32, 0x33, 0x2e, 0x30, 0x20, 0x71, 0x63, 0x6f, 0x6d, 0x70, 0x3d, 0x30, + 0x2e, 0x36, 0x30, 0x20, 0x71, 0x70, 0x6d, 0x69, 0x6e, 0x3d, 0x30, 0x20, + 0x71, 0x70, 0x6d, 0x61, 0x78, 0x3d, 0x36, 0x39, 0x20, 0x71, 0x70, 0x73, + 0x74, 0x65, 0x70, 0x3d, 0x34, 0x20, 0x69, 0x70, 0x5f, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x3d, 0x31, 0x2e, 0x34, 0x30, 0x20, 0x61, 0x71, 0x3d, 0x31, + 0x3a, 0x31, 0x2e, 0x30, 0x30, 0x00, 0x80, 0x00, 0x00, 0x04, 0xfb, 0x65, + 0x88, 0x84, 0x00, 0x25, 0xff, 0xd7, 0x2b, 0x47, 0x53, 0x79, 0x61, 0xc8, + 0xdf, 0x88, 0x8e, 0xdc, 0xef, 0xc0, 0x85, 0x00, 0x99, 0x10, 0x58, 0x46, + 0xf1, 0x28, 0xf2, 0x0c, 0xfc, 0x41, 0x82, 0xa0, 0x45, 0x81, 0xb5, 0x7e, + 0xcc, 0x27, 0x62, 0x2f, 0xac, 0x5c, 0xe8, 0xa9, 0xcf, 0x0e, 0x54, 0x17, + 0x68, 0x13, 0x48, 0x0d, 0xa1, 0x5d, 0xd1, 0x8b, 0xdf, 0x6e, 0xd0, 0xc8, + 0x8f, 0xb3, 0xc2, 0xaf, 0xd1, 0xdc, 0x98, 0x74, 0x7e, 0x0f, 0x69, 0xbe, + 0x54, 0xa2, 0x83, 0x4a, 0xc4, 0xf1, 0x79, 0x35, 0x60, 0x10, 0xb4, 0xfb, + 0xa7, 0x52, 0x3a, 0x99, 0x5e, 0x39, 0x0d, 0x04, 0xad, 0x24, 0x49, 0xfd, + 0xbe, 0x5f, 0x49, 0xdf, 0x36, 0x91, 0x1f, 0x62, 0x7f, 0x00, 0xa1, 0xba, + 0x3c, 0xe9, 0xf3, 0x38, 0x08, 0xb7, 0x87, 0x9a, 0x11, 0xae, 0x5f, 0xf2, + 0x66, 0x14, 0xc3, 0xa9, 0xf0, 0x76, 0x69, 0x5f, 0x1d, 0x20, 0x19, 0xc0, + 0x50, 0x94, 0xb4, 0x7b, 0x44, 0xaf, 0x21, 0x24, 0x80, 0x3f, 0x2a, 0x81, + 0x1b, 0xe4, 0xae, 0xfe, 0xb0, 0x99, 0xd3, 0xa5, 0x1d, 0x3f, 0x27, 0x0c, + 0x61, 0x54, 0x4d, 0x39, 0x8b, 0x1d, 0x12, 0x65, 0xe1, 0xb3, 0x4d, 0xc1, + 0x9d, 0xef, 0xfe, 0x48, 0xc6, 0xc9, 0xf4, 0x9a, 0x09, 0xc1, 0x3c, 0x5c, + 0xe3, 0xa1, 0x16, 0xec, 0x83, 0xb6, 0xb9, 0xc2, 0x94, 0x64, 0xc9, 0xf2, + 0x91, 0x51, 0x4c, 0xaf, 0xe5, 0xc3, 0x8a, 0x99, 0x1b, 0x22, 0x86, 0x55, + 0x70, 0xbf, 0xc7, 0x99, 0xc3, 0x07, 0x15, 0xd6, 0xdb, 0x27, 0x46, 0x08, + 0x19, 0x7e, 0x0a, 0x9b, 0x82, 0xe0, 0x15, 0x1d, 0x57, 0x8a, 0x2c, 0xc4, + 0x05, 0xf8, 0x09, 0xfb, 0x3c, 0x47, 0x4f, 0x55, 0xc6, 0x3e, 0x0b, 0x9d, + 0xe5, 0x03, 0x3e, 0xc9, 0x4c, 0xeb, 0xa2, 0x1b, 0x04, 0xfa, 0x99, 0x44, + 0x29, 0x06, 0x2e, 0x57, 0x61, 0xa3, 0x63, 0x00, 0x53, 0x08, 0x20, 0xcb, + 0x15, 0x50, 0x95, 0x13, 0x24, 0x63, 0xc9, 0x4f, 0xc0, 0xb0, 0x88, 0xe6, + 0x38, 0xef, 0x96, 0x99, 0xd2, 0x96, 0x47, 0xc7, 0xc8, 0x61, 0xac, 0xcc, + 0x24, 0x0a, 0x1f, 0x79, 0xce, 0x91, 0x88, 0xf5, 0xe9, 0xe2, 0x2d, 0x88, + 0xaf, 0x37, 0xca, 0xa3, 0x90, 0x5b, 0x83, 0x1a, 0x9c, 0x92, 0xda, 0x33, + 0x69, 0xe6, 0x7c, 0x14, 0xa6, 0x85, 0x00, 0x42, 0x46, 0x51, 0xcf, 0x50, + 0xed, 0x34, 0xe8, 0x8e, 0xb5, 0x78, 0xeb, 0x09, 0x37, 0x6e, 0x82, 0xea, + 0x7b, 0xe1, 0xfd, 0x45, 0x61, 0x77, 0x14, 0x6a, 0x75, 0xbf, 0xb8, 0x13, + 0x10, 0xc0, 0x08, 0xca, 0x1c, 0x51, 0xc7, 0x3a, 0x0a, 0x2b, 0x89, 0x5d, + 0x3c, 0x8d, 0xf4, 0x11, 0xc0, 0x3a, 0x90, 0x02, 0xc0, 0x0c, 0xe8, 0x59, + 0x11, 0xac, 0xe0, 0x5d, 0xe2, 0x95, 0x78, 0x09, 0xd5, 0xfa, 0x26, 0xf5, + 0x23, 0x30, 0x0a, 0xfd, 0x54, 0x0b, 0x73, 0x1c, 0x81, 0x22, 0x48, 0x8b, + 0x79, 0xe0, 0xf9, 0xe8, 0xde, 0x7a, 0x79, 0xba, 0xef, 0xdd, 0x5a, 0xd6, + 0x44, 0xb7, 0xa8, 0x41, 0xd0, 0xa1, 0xeb, 0x45, 0xf8, 0x4d, 0x7f, 0x97, + 0x48, 0x82, 0xd2, 0x81, 0x61, 0x08, 0x15, 0x9e, 0xfe, 0x78, 0xba, 0xdf, + 0xb0, 0x13, 0x92, 0x59, 0x4e, 0x4d, 0xfd, 0xff, 0x5d, 0x66, 0x14, 0xd4, + 0x0b, 0x43, 0x9f, 0xd8, 0x62, 0x4e, 0x54, 0x83, 0xf9, 0x59, 0x48, 0x2f, + 0x26, 0x21, 0xd4, 0xf0, 0x98, 0x6b, 0x14, 0x61, 0x4b, 0xf6, 0x00, 0xcf, + 0xe3, 0x24, 0x4b, 0x2f, 0xd4, 0x5e, 0x6d, 0x40, 0x4b, 0x52, 0xea, 0xa5, + 0x89, 0x94, 0x0f, 0xd2, 0xeb, 0x02, 0x54, 0x68, 0x26, 0xcf, 0x2d, 0x83, + 0x0f, 0x62, 0x1b, 0x9e, 0x75, 0x81, 0x65, 0x30, 0xdd, 0x03, 0xb0, 0xc1, + 0xda, 0x4a, 0xc9, 0xd8, 0x64, 0x29, 0x6a, 0xe2, 0x83, 0xf1, 0xb8, 0x4e, + 0xc5, 0xaf, 0x8e, 0xf0, 0x8c, 0xbb, 0xda, 0xea, 0x8a, 0xbc, 0xcc, 0xa3, + 0xf2, 0x19, 0xe8, 0xeb, 0x7e, 0x18, 0x64, 0x91, 0x1f, 0xdd, 0xc8, 0xc8, + 0xf8, 0xad, 0xc8, 0x3b, 0xf9, 0x92, 0x74, 0x03, 0x9b, 0x90, 0x51, 0xb7, + 0xb5, 0xe3, 0x80, 0x4f, 0x8a, 0x84, 0xfa, 0xa4, 0xd6, 0x9c, 0x53, 0x0b, + 0x81, 0xc3, 0xee, 0x9e, 0x70, 0xa3, 0xbd, 0x3f, 0xbb, 0x60, 0x2d, 0x97, + 0x65, 0xa7, 0x69, 0x2f, 0x22, 0x49, 0x65, 0xf4, 0xb0, 0x33, 0xbc, 0xd2, + 0x80, 0x1b, 0x2c, 0x3a, 0xe3, 0x04, 0x8c, 0x49, 0x42, 0x25, 0xa0, 0x6d, + 0x3c, 0xfe, 0xfa, 0x70, 0x90, 0x6a, 0x30, 0xf4, 0x0c, 0xe4, 0x3f, 0x78, + 0xf9, 0xba, 0x55, 0xb9, 0xfa, 0xd7, 0xce, 0x05, 0xbc, 0xe9, 0xc9, 0xad, + 0x4a, 0x37, 0xa0, 0xf7, 0x8d, 0x96, 0x22, 0xf6, 0x38, 0x8d, 0xf4, 0xf6, + 0xe6, 0x8b, 0x45, 0xac, 0x13, 0xc5, 0xe6, 0x05, 0x1e, 0x09, 0xd7, 0x98, + 0xb3, 0xb6, 0x59, 0xe4, 0x3b, 0x47, 0x16, 0x6e, 0xdf, 0xac, 0x7f, 0x38, + 0x6e, 0xf9, 0xcf, 0xaa, 0x68, 0x98, 0xdb, 0x22, 0x89, 0x6e, 0xad, 0xbe, + 0xed, 0xb1, 0x82, 0xa0, 0xc2, 0x9b, 0xd5, 0x79, 0x89, 0x96, 0xf6, 0xd9, + 0x8f, 0x58, 0x77, 0x15, 0x2c, 0x73, 0xeb, 0x89, 0xcc, 0xf0, 0x37, 0x4d, + 0x41, 0x70, 0xc5, 0x58, 0xae, 0x77, 0xab, 0x30, 0xcf, 0x6c, 0x7c, 0x1c, + 0x52, 0x9a, 0x62, 0xf6, 0xf8, 0x0a, 0x65, 0x92, 0x83, 0x01, 0xc3, 0x60, + 0xed, 0xfd, 0x4d, 0x9a, 0x4b, 0xd4, 0xa5, 0xe1, 0xc4, 0xe2, 0xe1, 0x8c, + 0x64, 0xce, 0x54, 0x9c, 0xa9, 0x7f, 0xb9, 0x34, 0x88, 0xb1, 0x17, 0xde, + 0x85, 0x27, 0x43, 0x81, 0x3b, 0x37, 0x27, 0x25, 0xbf, 0xba, 0x6c, 0x69, + 0xb4, 0xce, 0xcd, 0xef, 0x7c, 0xee, 0x48, 0xb9, 0x8a, 0x09, 0x1f, 0x42, + 0x8e, 0xc3, 0x14, 0xe4, 0xfd, 0xda, 0xa0, 0xfa, 0x7d, 0x0b, 0x68, 0x6a, + 0x81, 0xb1, 0x96, 0xf9, 0x07, 0xf2, 0xed, 0x32, 0xf3, 0x52, 0x15, 0x00, + 0x2f, 0x5f, 0x6e, 0x55, 0xc6, 0x85, 0x4b, 0xdc, 0x3d, 0x7c, 0xa6, 0xa7, + 0xeb, 0x80, 0xab, 0xf3, 0xfe, 0x21, 0xe6, 0x1d, 0xbd, 0xca, 0x33, 0x29, + 0x9c, 0x94, 0xa4, 0x7f, 0xec, 0x67, 0x6c, 0xc0, 0x0e, 0xea, 0x5f, 0x9a, + 0x30, 0x54, 0x9c, 0xf2, 0x8f, 0xaa, 0x5f, 0xc3, 0x3e, 0x61, 0x54, 0x41, + 0x8a, 0xbf, 0x7f, 0xff, 0x8a, 0xfb, 0x7f, 0x8c, 0x40, 0xe4, 0x5a, 0xbe, + 0xe5, 0x39, 0xa4, 0xdd, 0x9b, 0xa6, 0xab, 0xd7, 0xee, 0x15, 0x32, 0x04, + 0xd1, 0xbd, 0x7c, 0xe4, 0x98, 0x3f, 0x3f, 0x40, 0x87, 0xbc, 0x01, 0x36, + 0xe7, 0xcd, 0x7b, 0xcf, 0xf7, 0xe9, 0x60, 0x1d, 0xea, 0xe2, 0x6e, 0x13, + 0x3c, 0xd0, 0x28, 0x2d, 0xa8, 0x9d, 0x25, 0x06, 0x99, 0xf8, 0xdc, 0x9f, + 0x9f, 0xc5, 0x5a, 0xfd, 0x8f, 0x31, 0xcf, 0xc3, 0xe5, 0xe6, 0xc9, 0x99, + 0xae, 0x56, 0x72, 0xe2, 0x2f, 0xdf, 0x2c, 0x0e, 0x7d, 0x51, 0xc8, 0x35, + 0x40, 0x23, 0x9e, 0x52, 0x44, 0x6f, 0x17, 0x40, 0x01, 0xd1, 0x85, 0x07, + 0x55, 0xef, 0x10, 0xc9, 0xe5, 0xb9, 0xef, 0xbf, 0xf4, 0xe5, 0x38, 0xd3, + 0x1f, 0x2b, 0x91, 0x51, 0x60, 0x75, 0xc8, 0x95, 0xcc, 0x9d, 0x5a, 0xfa, + 0x69, 0xae, 0x9a, 0x16, 0x41, 0x07, 0x9c, 0x94, 0x40, 0xb7, 0xa9, 0xdd, + 0x8d, 0xcb, 0xce, 0xc5, 0xf2, 0x5e, 0x59, 0x67, 0x69, 0x07, 0xd3, 0x04, + 0xa4, 0x99, 0x56, 0xd9, 0xdc, 0x04, 0x9a, 0x66, 0x62, 0x7b, 0x67, 0xc8, + 0xd0, 0x34, 0x69, 0x5b, 0x4a, 0xce, 0x6e, 0x53, 0x0e, 0x62, 0xf7, 0x85, + 0xe0, 0xd7, 0xb7, 0x27, 0x55, 0x3a, 0x4d, 0x36, 0x47, 0xf2, 0x74, 0x02, + 0xb3, 0x66, 0x2c, 0xda, 0xc3, 0xb3, 0x38, 0x94, 0x67, 0x82, 0x44, 0xf4, + 0x12, 0xe4, 0x1c, 0x8f, 0x22, 0x4d, 0x32, 0x35, 0xf0, 0xe3, 0x41, 0x0a, + 0x7d, 0xe4, 0xb4, 0x6e, 0x10, 0x4f, 0xa9, 0x46, 0xd5, 0xab, 0x90, 0x4c, + 0xad, 0x2c, 0x30, 0xd0, 0x9e, 0x68, 0x2c, 0xc4, 0x3c, 0xf7, 0x05, 0xdf, + 0x22, 0xaa, 0xb0, 0x82, 0xbb, 0x2c, 0x67, 0x8c, 0xfd, 0x1b, 0x04, 0x41, + 0xf1, 0x4f, 0x77, 0xa4, 0xdb, 0xfb, 0xca, 0x1d, 0xd7, 0x61, 0x8a, 0x3e, + 0x89, 0x40, 0x88, 0xf2, 0xda, 0x35, 0x2b, 0x9d, 0xbf, 0xd8, 0x98, 0x55, + 0x4e, 0x60, 0xac, 0xc1, 0x1b, 0xd4, 0xe0, 0xb8, 0x6d, 0x13, 0xa0, 0xa3, + 0x24, 0x80, 0xa0, 0xe6, 0x12, 0xad, 0x27, 0x36, 0xee, 0xd7, 0x55, 0x4b, + 0xb4, 0x1a, 0xd2, 0x87, 0x31, 0x1a, 0x00, 0x53, 0xe9, 0x0f, 0xb7, 0x50, + 0xeb, 0xdb, 0x63, 0xfe, 0xc3, 0xd0, 0xb1, 0x25, 0xdc, 0x63, 0x66, 0xcc, + 0xe6, 0x99, 0xa3, 0x34, 0x0b, 0x1d, 0xdd, 0x84, 0x88, 0x3c, 0xfc, 0x79, + 0xf5, 0x13, 0x0a, 0xe0, 0xca, 0x9e, 0x02, 0xeb, 0x06, 0xab, 0x6d, 0x80, + 0xeb, 0x06, 0x3d, 0x9a, 0xbb, 0x97, 0xd5, 0xd2, 0x23, 0x22, 0x17, 0xca, + 0x7a, 0x34, 0x09, 0xfe, 0x53, 0xfa, 0xc1, 0x34, 0x2a, 0x2c, 0xcb, 0x07, + 0xd3, 0x92, 0x86, 0x9c, 0x7b, 0xd6, 0xdc, 0xe9, 0x5d, 0xa9, 0xcd, 0xb3, + 0x72, 0xc1, 0x5d, 0xcd, 0x3f, 0xc2, 0x9b, 0xcf, 0x5a, 0x54, 0xbe, 0x50, + 0x84, 0xbc, 0xe1, 0x33, 0xbd, 0xfd, 0xb6, 0x59, 0x49, 0x11, 0x25, 0xc9, + 0x01, 0x57, 0x78, 0x8a, 0xef, 0x16, 0x7e, 0x15, 0x2d, 0x9d, 0x30, 0x4b, + 0x68, 0xa4, 0x3b, 0x99, 0xbf, 0x11, 0x70, 0x0f, 0x17, 0x33, 0xd5, 0x6e, + 0x31, 0x86, 0x7a, 0xea, 0x12, 0xdb, 0xb9, 0xbc, 0x67, 0x4e, 0x79, 0x58, + 0x2f, 0x81, 0x00, 0x00, 0x00, 0x0e, 0x41, 0x9a, 0x21, 0x6c, 0x42, 0xdf, + 0x18, 0xd3, 0x2d, 0x01, 0x43, 0x7f, 0x24, 0x38, 0x00, 0x00, 0x03, 0x1f, + 'm', 'o', 'o', 'v', 0x00, 0x00, 0x00, 0x6c, 'm', 'v', 'h', 'd', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x49, 't', 'r', 'a', 'k', + 0x00, 0x00, 0x00, 0x5c, 't', 'k', 'h', 'd', 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0xa0, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, + 'e', 'd', 't', 's', 0x00, 0x00, 0x00, 0x1c, 'e', 'l', 's', 't', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc1, + 'm', 'd', 'i', 'a', 0x00, 0x00, 0x00, 0x20, 'm', 'd', 'h', 'd', + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x04, 0x00, 0x15, 0xc7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2d, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65, + 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x01, + 0x6c, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x14, 0x76, 0x6d, 0x68, + 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, + 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x2c, 0x73, 0x74, 0x62, 0x6c, 0x00, 0x00, 0x00, + 0xac, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x9c, 0x61, 0x76, 0x63, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x5a, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x36, 0x61, 0x76, 0x63, 0x43, 0x01, + 0x64, 0x00, 0x0a, 0xff, 0xe1, 0x00, 0x1d, 0x67, 0x64, 0x00, 0x0a, 0xac, + 0xd9, 0x42, 0x0d, 0xf9, 0x3f, 0xf0, 0x00, 0x50, 0x00, 0x41, 0x00, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x32, 0x0f, 0x12, 0x25, 0x96, + 0x01, 0x00, 0x06, 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0, 0x00, 0x00, 0x00, + 0x10, 0x70, 0x61, 0x73, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x18, 0x73, 0x74, 0x74, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x73, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x1c, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1c, 0x73, 0x74, 0x73, 0x7a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x07, + 0xb2, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x63, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x62, 0x75, 0x64, 0x74, 0x61, 0x00, 0x00, 0x00, + 0x5a, 0x6d, 0x65, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x68, 0x64, 0x6c, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x6d, 0x64, 0x69, 0x72, 0x61, 0x70, 0x70, 0x6c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x69, 0x6c, + 0x73, 0x74, 0x00, 0x00, 0x00, 0x25, 0xa9, 0x74, 0x6f, 0x6f, 0x00, 0x00, + 0x00, 0x1d, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x4c, 0x61, 0x76, 0x66, 0x35, 0x36, 0x2e, 0x33, 0x33, 0x2e, + 0x31, 0x30, 0x31 +}; +static const size_t test_case_mp4_len = ArrayLength(test_case_mp4); + +TEST(stagefright_MPEG4Metadata, test_case_mp4) +{ + nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len); + buffer->AppendElements(test_case_mp4, test_case_mp4_len); + nsRefPtr<BufferStream> stream = new BufferStream(buffer); + + EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream)); + nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); + EXPECT_TRUE(metadataBuffer); + + MP4Metadata metadata(stream); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kUndefinedTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kAudioTrack)); + EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(TrackInfo::kTextTrack)); + EXPECT_EQ(0u, metadata.GetNumberTracks(static_cast<TrackInfo::TrackType>(-1))); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kUndefinedTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kAudioTrack, 0)); + UniquePtr<TrackInfo> track = metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0); + EXPECT_TRUE(!!track); + EXPECT_FALSE(metadata.GetTrackInfo(TrackInfo::kTextTrack, 0)); + EXPECT_FALSE(metadata.GetTrackInfo(static_cast<TrackInfo::TrackType>(-1), 0)); + // We can see anywhere in any MPEG4. + EXPECT_TRUE(metadata.CanSeek()); + EXPECT_FALSE(metadata.Crypto().valid); +} + +TEST(stagefright_MPEG4Metadata, test_case_mp4_skimming) +{ + static const size_t step = 4u; + nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len); + buffer->AppendElements(test_case_mp4, test_case_mp4_len); + for (size_t offset = 0; offset < test_case_mp4_len - step; offset += step) { + nsRefPtr<BufferStream> stream = new BufferStream(buffer); + + // Just exercizing the parser starting at different points through the file, + // making sure it doesn't crash. + // No checks because results would differ for each position. + MP4Metadata::HasCompleteMetadata(stream); + nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream); + MP4Metadata metadata(stream); + + buffer->RemoveElementsAt(0, step); + } +} + +TEST(stagefright_MoofParser, test_case_mp4) +{ + nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len); + buffer->AppendElements(test_case_mp4, test_case_mp4_len); + nsRefPtr<BufferStream> stream = new BufferStream(buffer); + + Monitor monitor("MP4Metadata::HasCompleteMetadata"); + MonitorAutoLock mon(monitor); + MoofParser parser(stream, 0, false, &monitor); + EXPECT_EQ(0u, parser.mOffset); + EXPECT_FALSE(parser.ReachedEnd()); + + nsTArray<MediaByteRange> byteRanges; + byteRanges.AppendElement(MediaByteRange(0, 0)); + EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges)); + + EXPECT_TRUE(parser.GetCompositionRange(byteRanges).IsNull()); + EXPECT_TRUE(parser.mInitRange.IsNull()); + EXPECT_EQ(0u, parser.mOffset); + EXPECT_FALSE(parser.ReachedEnd()); + EXPECT_TRUE(parser.HasMetadata()); + nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata(); + EXPECT_TRUE(metadataBuffer); + EXPECT_TRUE(parser.FirstCompleteMediaSegment().IsNull()); + EXPECT_TRUE(parser.FirstCompleteMediaHeader().IsNull()); +} + +TEST(stagefright_MoofParser, test_case_mp4_skimming) +{ + const size_t step = 4u; + nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(test_case_mp4_len); + buffer->AppendElements(test_case_mp4, test_case_mp4_len); + Monitor monitor("MP4Metadata::HasCompleteMetadata"); + MonitorAutoLock mon(monitor); + for (size_t offset = 0; offset < test_case_mp4_len - step; offset += step) { + nsRefPtr<BufferStream> stream = new BufferStream(buffer); + + // Just exercizing the parser starting at different points through the file, + // making sure it doesn't crash. + // No checks because results would differ for each position. + MoofParser parser(stream, 0, false, &monitor); + nsTArray<MediaByteRange> byteRanges; + byteRanges.AppendElement(MediaByteRange(0, 0)); + EXPECT_FALSE(parser.RebuildFragmentedIndex(byteRanges)); + parser.GetCompositionRange(byteRanges); + parser.HasMetadata(); + nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata(); + parser.FirstCompleteMediaSegment(); + parser.FirstCompleteMediaHeader(); + + buffer->RemoveElementsAt(0, step); + } +}
--- a/memory/build/jemalloc_config.cpp +++ b/memory/build/jemalloc_config.cpp @@ -32,17 +32,17 @@ #endif #ifdef DEBUG #define MOZ_MALLOC_BUILD_OPTIONS ",junk:true" #else #define MOZ_MALLOC_BUILD_OPTIONS ",junk:free" #endif -#define MOZ_MALLOC_OPTIONS "narenas:1,lg_chunk:20,tcache:false" +#define MOZ_MALLOC_OPTIONS "narenas:1,tcache:false" MFBT_DATA const char* je_(malloc_conf) = MOZ_MALLOC_OPTIONS MOZ_MALLOC_PLATFORM_OPTIONS MOZ_MALLOC_BUILD_OPTIONS; #ifdef ANDROID #include <android/log.h> static void _je_malloc_message(void* cbopaque, const char* s)
--- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -602,24 +602,30 @@ pref("shumway.disabled", true); #endif // enable touch events interfaces pref("dom.w3c_touch_events.enabled", 1); #ifdef MOZ_SAFE_BROWSING pref("browser.safebrowsing.enabled", true); pref("browser.safebrowsing.malware.enabled", true); +pref("browser.safebrowsing.downloads.enabled", false); +pref("browser.safebrowsing.downloads.remote.enabled", false); +pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000); pref("browser.safebrowsing.debug", false); -pref("browser.safebrowsing.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); -pref("browser.safebrowsing.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,goog-malware-shavar,goog-unwanted-shavar"); +pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2&key=%GOOGLE_API_KEY%"); +pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); +pref("browser.safebrowsing.provider.google.appRepURL", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%"); + pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url="); pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url="); -pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site="); pref("browser.safebrowsing.id", @MOZ_APP_UA_NAME@); // Name of the about: page contributed by safebrowsing to handle display of error // pages on phishing/malware hits. (bug 399233) pref("urlclassifier.alternate_error_page", "blocked"); // The number of random entries to send with a gethash request.
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1430,16 +1430,21 @@ pref("network.http.enforce-framing.http1 pref("network.http.enforce-framing.soft", true); // Whether nsHttpChannel should use the PackagedAppService to load // resources from a package when directed to a URL // such as http://domain.com/package.pak!//resource.html // See http://www.w3.org/TR/web-packaging/#streamable-package-format pref("network.http.enable-packaged-apps", false); +// Enable this pref to skip verification process. The packaged app +// will be considered signed no matter the package has a valid/invalid +// signature or no signature. +pref("network.http.packaged-apps-developer-mode", false); + // default values for FTP // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594, // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22) // per Section 4.7 "Low-Latency Data Service Class". pref("network.ftp.data.qos", 0); pref("network.ftp.control.qos", 0); // If this pref is false only one xpcom event will be served per poll @@ -4758,18 +4763,20 @@ pref("urlclassifier.phishTable", "goog-p pref("urlclassifier.downloadBlockTable", ""); pref("urlclassifier.downloadAllowTable", ""); pref("urlclassifier.disallow_completions", "test-malware-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,goog-downloadwhite-digest256,mozpub-track-digest256,mozpub-trackwhite-digest256"); // The table and update/gethash URLs for Safebrowsing phishing and malware // checks. pref("urlclassifier.trackingTable", "test-track-simple,mozpub-track-digest256"); pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozpub-trackwhite-digest256"); -pref("browser.trackingprotection.updateURL", "https://tracking.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); -pref("browser.trackingprotection.gethashURL", "https://tracking.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); + +pref("browser.safebrowsing.provider.mozilla.lists", "mozpub-track-digest256,mozpub-trackwhite-digest256"); +pref("browser.safebrowsing.provider.mozilla.updateURL", "https://tracking.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); +pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://tracking.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2"); // Turn off Spatial navigation by default. pref("snav.enabled", false); // Original caret implementation on collapsed selection. pref("touchcaret.enabled", false); // This will inflate the size of the touch caret frame when checking if user
--- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -64,16 +64,17 @@ XPIDL_SOURCES += [ 'nsINetworkLinkService.idl', 'nsINetworkPredictor.idl', 'nsINetworkPredictorVerifier.idl', 'nsINetworkProperties.idl', 'nsINSSErrorsService.idl', 'nsINullChannel.idl', 'nsIPACGenerator.idl', 'nsIPackagedAppService.idl', + 'nsIPackagedAppVerifier.idl', 'nsIParentChannel.idl', 'nsIParentRedirectingChannel.idl', 'nsIPermission.idl', 'nsIPermissionManager.idl', 'nsIPrivateBrowsingChannel.idl', 'nsIProgressEventSink.idl', 'nsIPrompt.idl', 'nsIProtocolHandler.idl',
--- a/netwerk/base/nsIMultiPartChannel.idl +++ b/netwerk/base/nsIMultiPartChannel.idl @@ -7,17 +7,17 @@ interface nsIChannel; /** * An interface to access the the base channel * associated with a MultiPartChannel. */ -[scriptable, uuid(3c329c90-2ee0-11e5-a2cb-0800200c9a66)] +[scriptable, uuid(4fefb490-5567-11e5-a837-0800200c9a66)] interface nsIMultiPartChannel : nsISupports { /** * readonly attribute to access the underlying channel */ readonly attribute nsIChannel baseChannel; /** @@ -33,9 +33,14 @@ interface nsIMultiPartChannel : nsISuppo */ readonly attribute boolean isLastPart; /** * ASCII-encoding content prior to the first resource. Only valid for * content-type=application/package. */ readonly attribute ACString preamble; + + /** + * The original http response header in each part. + */ + readonly attribute ACString originalResponseHeader; };
new file mode 100644 --- /dev/null +++ b/netwerk/base/nsIPackagedAppVerifier.idl @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsIStreamListener.idl" + +interface nsIURI; +interface nsICacheEntry; +interface nsIPackagedAppVerifierListener; + +/** + * nsIPackagedAppVerifier + * + * It inherits nsIStreamListener and all the data will be fed by + * onStartRequest/onDataAvailable/onStopRequest. + * + */ +[scriptable, uuid(16419a80-4cc3-11e5-b970-0800200c9a66)] +interface nsIPackagedAppVerifier : nsIStreamListener +{ + // The package origin of either a signed or unsigned package. + readonly attribute ACString packageOrigin; + + // Whether this package is signed. + readonly attribute boolean isPackageSigned; + + /** + * @param aListener + * an object implementing nsIPackagedAppVerifierListener as the bridge that + * the client gets callback from the package verifier. The callback might be + * sync or async depending on the implementation. + * + * @param aPackageOrigin + * the origin of the package. It will be updated based on the package + * identifier defined in the manifest. + * + * @param aSignature + * the signature of the package we desire to verify against. See + * https://wiki.mozilla.org/User:Ptheriault/Packagedprivilegedcontent#The_Signed_Manifest + * for further information. + * + * @param aPackageCacheEntry + * the cache entry of the package itself (not the resource's cache). + * It will be used to store any necessary information like the signed + * package origin. + * + * The verifier init function. + */ + void init(in nsIPackagedAppVerifierListener aListener, + in ACString aPackageOrigin, + in ACString aSignature, + in nsICacheEntry aPackageCacheEntry); + + /** + * @param aUri + * the URI of the resource. + * + * @param aCacheEntry + * the cache entry of the resource. + * + * @param aStatusCode + * the status code of the resource we just finished download. + * + * @param aIsLastPart + * whether this resource is the last one in the package. + * + * Create an object that we will pass to the verifier as a user context + * through onStartRequest. The main purpose of this function is to make + * nsIPackagedAppVerifier xpcshell-testable. See test_packaged_app_verifier.js. + * + */ + nsISupports createResourceCacheInfo(in nsIURI aUri, + in nsICacheEntry aCacheEntry, + in nsresult aStatusCode, + in boolean aIsLastPart); +}; + +/** + * nsIPackagedAppVerifierListener + */ +[scriptable, uuid(092eba70-4cbf-11e5-b970-0800200c9a66)] +interface nsIPackagedAppVerifierListener : nsISupports +{ + /** + * @param aIsManifest + * indicate if this callback is for manifest or not. True for manifest and false + * for resource. + * + * @param aUri + * the URI of the resource that has just been verified. + * + * @param aCacheEntry + * the cache entry of the resource that has just been verified. + * + * @param aStatusCode + * the resource download status code from nsIMultipartChannel. + * + * @param aIsLastPart + * indicate if the verified resource is that last one in the package. + * + * @param aVerificationSuccess + * the verification result. + * + * Callback'ed when a manifest/resource is verified. + */ + void onVerified(in boolean aIsManifest, + in nsIURI aUri, + in nsICacheEntry aCacheEntry, + in nsresult aStatusCode, + in boolean aIsLastPart, + in boolean aVerificationSuccess); +}; +
--- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -901,16 +901,25 @@ #define NS_PACKAGEDAPPSERVICE_CID \ { /* adef6762-41b9-4470-a06a-dc29cf8de381 */ \ 0xadef6762, \ 0x41b9, \ 0x4470, \ { 0xa0, 0x6a, 0xdc, 0x29, 0xcf, 0x8d, 0xe3, 0x81 } \ } +#define NS_PACKAGEDAPPVERIFIER_CONTRACTID \ + "@mozilla.org/network/packaged-app-verifier;1" +#define NS_PACKAGEDAPPVERIFIER_CID \ +{ /* 07242d20-4cae-11e5-b970-0800200c9a66 */ \ + 0x07242d20, \ + 0x4cae, \ + 0x11e5, \ + { 0xb9, 0x70, 0x08, 0x00, 0x20, 0x0c, 0x96, 0x66 } \ +} /****************************************************************************** * netwerk/cookie classes */ // service implementing nsICookieManager and nsICookieManager2. #define NS_COOKIEMANAGER_CONTRACTID \ "@mozilla.org/cookiemanager;1"
--- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -259,20 +259,22 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpAct NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth) } // namespace net } // namespace mozilla #endif // !NECKO_PROTOCOL_http #include "mozilla/net/Dashboard.h" #include "mozilla/net/PackagedAppService.h" +#include "mozilla/net/PackagedAppVerifier.h" namespace mozilla { namespace net { NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard) NS_GENERIC_FACTORY_CONSTRUCTOR(PackagedAppService) + NS_GENERIC_FACTORY_CONSTRUCTOR(PackagedAppVerifier) } // namespace net } // namespace mozilla #include "AppProtocolHandler.h" #ifdef NECKO_PROTOCOL_res // resource #include "nsResProtocolHandler.h" #include "ExtensionProtocolHandler.h" @@ -727,16 +729,17 @@ NS_DEFINE_NAMED_CID(NS_AUTHURLPARSER_CID NS_DEFINE_NAMED_CID(NS_STANDARDURL_CID); NS_DEFINE_NAMED_CID(NS_ARRAYBUFFERINPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_BUFFEREDINPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_BUFFEREDOUTPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_MIMEINPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_PROTOCOLPROXYSERVICE_CID); NS_DEFINE_NAMED_CID(NS_STREAMCONVERTERSERVICE_CID); NS_DEFINE_NAMED_CID(NS_PACKAGEDAPPSERVICE_CID); +NS_DEFINE_NAMED_CID(NS_PACKAGEDAPPVERIFIER_CID); NS_DEFINE_NAMED_CID(NS_DASHBOARD_CID); #ifdef NECKO_PROTOCOL_ftp NS_DEFINE_NAMED_CID(NS_FTPDIRLISTINGCONVERTER_CID); #endif NS_DEFINE_NAMED_CID(NS_NSINDEXEDTOHTMLCONVERTER_CID); NS_DEFINE_NAMED_CID(NS_DIRINDEXPARSER_CID); NS_DEFINE_NAMED_CID(NS_MULTIMIXEDCONVERTER_CID); NS_DEFINE_NAMED_CID(NS_UNKNOWNDECODER_CID); @@ -875,16 +878,17 @@ static const mozilla::Module::CIDEntry k { &kNS_STANDARDURL_CID, false, nullptr, nsStandardURLConstructor }, { &kNS_ARRAYBUFFERINPUTSTREAM_CID, false, nullptr, ArrayBufferInputStreamConstructor }, { &kNS_BUFFEREDINPUTSTREAM_CID, false, nullptr, nsBufferedInputStream::Create }, { &kNS_BUFFEREDOUTPUTSTREAM_CID, false, nullptr, nsBufferedOutputStream::Create }, { &kNS_MIMEINPUTSTREAM_CID, false, nullptr, nsMIMEInputStreamConstructor }, { &kNS_PROTOCOLPROXYSERVICE_CID, true, nullptr, nsProtocolProxyServiceConstructor }, { &kNS_STREAMCONVERTERSERVICE_CID, false, nullptr, CreateNewStreamConvServiceFactory }, { &kNS_PACKAGEDAPPSERVICE_CID, false, NULL, mozilla::net::PackagedAppServiceConstructor }, + { &kNS_PACKAGEDAPPVERIFIER_CID, false, NULL, mozilla::net::PackagedAppVerifierConstructor }, { &kNS_DASHBOARD_CID, false, nullptr, mozilla::net::DashboardConstructor }, #ifdef NECKO_PROTOCOL_ftp { &kNS_FTPDIRLISTINGCONVERTER_CID, false, nullptr, CreateNewFTPDirListingConv }, #endif { &kNS_NSINDEXEDTOHTMLCONVERTER_CID, false, nullptr, nsIndexedToHTML::Create }, { &kNS_DIRINDEXPARSER_CID, false, nullptr, nsDirIndexParserConstructor }, { &kNS_MULTIMIXEDCONVERTER_CID, false, nullptr, CreateNewMultiMixedConvFactory }, { &kNS_UNKNOWNDECODER_CID, false, nullptr, CreateNewUnknownDecoderFactory }, @@ -1025,16 +1029,17 @@ static const mozilla::Module::ContractID { NS_STANDARDURL_CONTRACTID, &kNS_STANDARDURL_CID }, { NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID, &kNS_ARRAYBUFFERINPUTSTREAM_CID }, { NS_BUFFEREDINPUTSTREAM_CONTRACTID, &kNS_BUFFEREDINPUTSTREAM_CID }, { NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &kNS_BUFFEREDOUTPUTSTREAM_CID }, { NS_MIMEINPUTSTREAM_CONTRACTID, &kNS_MIMEINPUTSTREAM_CID }, { NS_PROTOCOLPROXYSERVICE_CONTRACTID, &kNS_PROTOCOLPROXYSERVICE_CID }, { NS_STREAMCONVERTERSERVICE_CONTRACTID, &kNS_STREAMCONVERTERSERVICE_CID }, { NS_PACKAGEDAPPSERVICE_CONTRACTID, &kNS_PACKAGEDAPPSERVICE_CID }, + { NS_PACKAGEDAPPVERIFIER_CONTRACTID, &kNS_PACKAGEDAPPVERIFIER_CID }, { NS_DASHBOARD_CONTRACTID, &kNS_DASHBOARD_CID }, #ifdef NECKO_PROTOCOL_ftp { NS_ISTREAMCONVERTER_KEY FTP_TO_INDEX, &kNS_FTPDIRLISTINGCONVERTER_CID }, #endif { NS_ISTREAMCONVERTER_KEY INDEX_TO_HTML, &kNS_NSINDEXEDTOHTMLCONVERTER_CID }, { NS_DIRINDEXPARSER_CONTRACTID, &kNS_DIRINDEXPARSER_CID }, { NS_ISTREAMCONVERTER_KEY MULTI_MIXED_X, &kNS_MULTIMIXEDCONVERTER_CID }, { NS_ISTREAMCONVERTER_KEY MULTI_BYTERANGES, &kNS_MULTIMIXEDCONVERTER_CID },
--- a/netwerk/cache2/AppCacheStorage.cpp +++ b/netwerk/cache2/AppCacheStorage.cpp @@ -20,17 +20,17 @@ namespace mozilla { namespace net { NS_IMPL_ISUPPORTS_INHERITED0(AppCacheStorage, CacheStorage) AppCacheStorage::AppCacheStorage(nsILoadContextInfo* aInfo, nsIApplicationCache* aAppCache) -: CacheStorage(aInfo, true /* disk */, false /* lookup app cache */) +: CacheStorage(aInfo, true /* disk */, false /* lookup app cache */, false /* skip size check */) , mAppCache(aAppCache) { MOZ_COUNT_CTOR(AppCacheStorage); } AppCacheStorage::~AppCacheStorage() { ProxyReleaseMainThread(mAppCache);
--- a/netwerk/cache2/CacheEntry.cpp +++ b/netwerk/cache2/CacheEntry.cpp @@ -158,25 +158,27 @@ nsresult CacheEntry::Callback::OnAvailTh NS_IMPL_ISUPPORTS(CacheEntry, nsICacheEntry, nsIRunnable, CacheFileListener) CacheEntry::CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID, - bool aUseDisk) + bool aUseDisk, + bool aSkipSizeCheck) : mFrecency(0) , mSortingExpirationTime(uint32_t(-1)) , mLock("CacheEntry") , mFileStatus(NS_ERROR_NOT_INITIALIZED) , mURI(aURI) , mEnhanceID(aEnhanceID) , mStorageID(aStorageID) , mUseDisk(aUseDisk) +, mSkipSizeCheck(aSkipSizeCheck) , mIsDoomed(false) , mSecurityInfoLoaded(false) , mPreventCallbacks(false) , mHasData(false) , mState(NOTLOADED) , mRegistration(NEVERREGISTERED) , mWriter(nullptr) , mPredictedDataSize(0) @@ -386,16 +388,17 @@ bool CacheEntry::Load(bool aTruncate, bo CacheFileUtils::DetailedCacheHitTelemetry::MISS, mLoadStart); } LOG((" performing load, file=%p", mFile.get())); if (NS_SUCCEEDED(rv)) { rv = mFile->Init(fileKey, aTruncate, !mUseDisk, + mSkipSizeCheck, aPriority, directLoad ? nullptr : this); } if (NS_FAILED(rv)) { mFileStatus = rv; AsyncDoom(nullptr); return false; @@ -481,16 +484,17 @@ already_AddRefed<CacheEntryHandle> Cache nsRefPtr<CacheEntry> newEntry; { mozilla::MutexAutoUnlock unlock(mLock); // The following call dooms this entry (calls DoomAlreadyRemoved on us) nsresult rv = CacheStorageService::Self()->AddStorageEntry( GetStorageID(), GetURI(), GetEnhanceID(), mUseDisk && !aMemoryOnly, + mSkipSizeCheck, true, // always create true, // truncate existing (this one) getter_AddRefs(handle)); if (NS_SUCCEEDED(rv)) { newEntry = handle->Entry(); LOG((" exchanged entry %p by entry %p, rv=0x%08x", this, newEntry.get(), rv)); newEntry->AsyncOpen(aCallback, nsICacheStorage::OPEN_TRUNCATE); @@ -1137,17 +1141,17 @@ NS_IMETHODIMP CacheEntry::GetPredictedDa { *aPredictedDataSize = mPredictedDataSize; return NS_OK; } NS_IMETHODIMP CacheEntry::SetPredictedDataSize(int64_t aPredictedDataSize) { mPredictedDataSize = aPredictedDataSize; - if (CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) { + if (!mSkipSizeCheck && CacheObserver::EntryIsTooBig(mPredictedDataSize, mUseDisk)) { LOG(("CacheEntry::SetPredictedDataSize [this=%p] too big, dooming", this)); AsyncDoom(nullptr); return NS_ERROR_FILE_TOO_BIG; } return NS_OK; }
--- a/netwerk/cache2/CacheEntry.h +++ b/netwerk/cache2/CacheEntry.h @@ -50,17 +50,17 @@ class CacheEntry final : public nsICache , public CacheFileListener { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICACHEENTRY NS_DECL_NSIRUNNABLE CacheEntry(const nsACString& aStorageID, nsIURI* aURI, const nsACString& aEnhanceID, - bool aUseDisk); + bool aUseDisk, bool aSkipSizeCheck); void AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags); CacheEntryHandle* NewHandle(); public: uint32_t GetMetadataMemoryConsumption(); nsCString const &GetStorageID() const { return mStorageID; } @@ -271,16 +271,19 @@ private: ::mozilla::Atomic<nsresult, ::mozilla::ReleaseAcquire> mFileStatus; nsCOMPtr<nsIURI> mURI; nsCString mEnhanceID; nsCString mStorageID; // Whether it's allowed to persist the data to disk bool const mUseDisk; + // Whether it should skip max size check. + bool const mSkipSizeCheck; + // Set when entry is doomed with AsyncDoom() or DoomAlreadyRemoved(). // Left as a standalone flag to not bother with locking (there is no need). bool mIsDoomed; // Following flags are all synchronized with the cache entry lock. // Whether security info has already been looked up in metadata. bool mSecurityInfoLoaded : 1;
--- a/netwerk/cache2/CacheFile.cpp +++ b/netwerk/cache2/CacheFile.cpp @@ -178,16 +178,17 @@ NS_INTERFACE_MAP_BEGIN(CacheFile) mozilla::net::CacheFileChunkListener) NS_INTERFACE_MAP_END_THREADSAFE CacheFile::CacheFile() : mLock("CacheFile.mLock") , mOpeningFile(false) , mReady(false) , mMemoryOnly(false) + , mSkipSizeCheck(false) , mOpenAsMemoryOnly(false) , mPriority(false) , mDataAccessed(false) , mDataIsDirty(false) , mWritingMetadata(false) , mPreloadWithoutInputStreams(true) , mPreloadChunkCount(0) , mStatus(NS_OK) @@ -207,26 +208,28 @@ CacheFile::~CacheFile() WriteMetadataIfNeededLocked(true); } } nsresult CacheFile::Init(const nsACString &aKey, bool aCreateNew, bool aMemoryOnly, + bool aSkipSizeCheck, bool aPriority, CacheFileListener *aCallback) { MOZ_ASSERT(!mListener); MOZ_ASSERT(!mHandle); nsresult rv; mKey = aKey; mOpenAsMemoryOnly = mMemoryOnly = aMemoryOnly; + mSkipSizeCheck = aSkipSizeCheck; mPriority = aPriority; // Some consumers (at least nsHTTPCompressConv) assume that Read() can read // such amount of data that was announced by Available(). // CacheFileInputStream::Available() uses also preloaded chunks to compute // number of available bytes in the input stream, so we have to make sure the // preloadChunkCount won't change during CacheFile's lifetime since otherwise // we could potentially release some cached chunks that was used to calculate
--- a/netwerk/cache2/CacheFile.h +++ b/netwerk/cache2/CacheFile.h @@ -51,16 +51,17 @@ class CacheFile final : public CacheFile public: NS_DECL_THREADSAFE_ISUPPORTS CacheFile(); nsresult Init(const nsACString &aKey, bool aCreateNew, bool aMemoryOnly, + bool aSkipSizeCheck, bool aPriority, CacheFileListener *aCallback); NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk) override; NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk) override; NS_IMETHOD OnChunkAvailable(nsresult aResult, uint32_t aChunkIdx, CacheFileChunk *aChunk) override; NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk) override; @@ -188,16 +189,17 @@ private: void SetError(nsresult aStatus); nsresult InitIndexEntry(); mozilla::Mutex mLock; bool mOpeningFile; bool mReady; bool mMemoryOnly; + bool mSkipSizeCheck; bool mOpenAsMemoryOnly; bool mPriority; bool mDataAccessed; bool mDataIsDirty; bool mWritingMetadata; bool mPreloadWithoutInputStreams; uint32_t mPreloadChunkCount; nsresult mStatus;
--- a/netwerk/cache2/CacheFileOutputStream.cpp +++ b/netwerk/cache2/CacheFileOutputStream.cpp @@ -89,17 +89,17 @@ CacheFileOutputStream::Write(const char if (mClosed) { LOG(("CacheFileOutputStream::Write() - Stream is closed. [this=%p, " "status=0x%08x]", this, mStatus)); return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED; } - if (CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) { + if (!mFile->mSkipSizeCheck && CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) { LOG(("CacheFileOutputStream::Write() - Entry is too big, failing and " "dooming the entry. [this=%p]", this)); mFile->DoomLocked(nullptr); CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG); return NS_ERROR_FILE_TOO_BIG; }
--- a/netwerk/cache2/CacheStorage.cpp +++ b/netwerk/cache2/CacheStorage.cpp @@ -20,20 +20,22 @@ namespace mozilla { namespace net { NS_IMPL_ISUPPORTS(CacheStorage, nsICacheStorage) CacheStorage::CacheStorage(nsILoadContextInfo* aInfo, bool aAllowDisk, - bool aLookupAppCache) + bool aLookupAppCache, + bool aSkipSizeCheck) : mLoadContextInfo(GetLoadContextInfo(aInfo)) , mWriteToDisk(aAllowDisk) , mLookupAppCache(aLookupAppCache) +, mSkipSizeCheck(aSkipSizeCheck) { } CacheStorage::~CacheStorage() { } NS_IMETHODIMP CacheStorage::AsyncOpenURI(nsIURI *aURI,
--- a/netwerk/cache2/CacheStorage.h +++ b/netwerk/cache2/CacheStorage.h @@ -47,29 +47,32 @@ private: class CacheStorage : public nsICacheStorage { NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICACHESTORAGE public: CacheStorage(nsILoadContextInfo* aInfo, bool aAllowDisk, - bool aLookupAppCache); + bool aLookupAppCache, + bool aSkipSizeCheck); protected: virtual ~CacheStorage(); nsresult ChooseApplicationCache(nsIURI* aURI, nsIApplicationCache** aCache); nsRefPtr<LoadContextInfo> mLoadContextInfo; bool mWriteToDisk : 1; bool mLookupAppCache : 1; + bool mSkipSizeCheck: 1; public: nsILoadContextInfo* LoadInfo() const { return mLoadContextInfo; } bool WriteToDisk() const { return mWriteToDisk && !mLoadContextInfo->IsPrivate(); } bool LookupAppCache() const { return mLookupAppCache; } + bool SkipSizeCheck() const { return mSkipSizeCheck; } }; } // namespace net } // namespace mozilla #endif
--- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -676,17 +676,17 @@ nsresult CacheStorageService::Dispatch(n NS_IMETHODIMP CacheStorageService::MemoryCacheStorage(nsILoadContextInfo *aLoadContextInfo, nsICacheStorage * *_retval) { NS_ENSURE_ARG(aLoadContextInfo); NS_ENSURE_ARG(_retval); nsCOMPtr<nsICacheStorage> storage; if (CacheObserver::UseNewCache()) { - storage = new CacheStorage(aLoadContextInfo, false, false); + storage = new CacheStorage(aLoadContextInfo, false, false, false); } else { storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr); } storage.forget(_retval); return NS_OK; } @@ -701,17 +701,17 @@ NS_IMETHODIMP CacheStorageService::DiskC // TODO save some heap granularity - cache commonly used storages. // When disk cache is disabled, still provide a storage, but just keep stuff // in memory. bool useDisk = CacheObserver::UseDiskCache(); nsCOMPtr<nsICacheStorage> storage; if (CacheObserver::UseNewCache()) { - storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache); + storage = new CacheStorage(aLoadContextInfo, useDisk, aLookupAppCache, false); } else { storage = new _OldStorage(aLoadContextInfo, useDisk, aLookupAppCache, false, nullptr); } storage.forget(_retval); return NS_OK; } @@ -732,16 +732,34 @@ NS_IMETHODIMP CacheStorageService::AppCa else { storage = new _OldStorage(aLoadContextInfo, true, false, true, aApplicationCache); } storage.forget(_retval); return NS_OK; } +NS_IMETHODIMP CacheStorageService::SynthesizedCacheStorage(nsILoadContextInfo *aLoadContextInfo, + nsICacheStorage * *_retval) +{ + NS_ENSURE_ARG(aLoadContextInfo); + NS_ENSURE_ARG(_retval); + + nsCOMPtr<nsICacheStorage> storage; + if (CacheObserver::UseNewCache()) { + storage = new CacheStorage(aLoadContextInfo, false, false, true /* skip size checks for synthesized cache */); + } + else { + storage = new _OldStorage(aLoadContextInfo, false, false, false, nullptr); + } + + storage.forget(_retval); + return NS_OK; +} + NS_IMETHODIMP CacheStorageService::Clear() { nsresult rv; if (CacheObserver::UseNewCache()) { { mozilla::MutexAutoLock lock(mLock); @@ -1334,25 +1352,27 @@ CacheStorageService::AddStorageEntry(Cac NS_ENSURE_FALSE(mShutdown, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_ARG(aStorage); nsAutoCString contextKey; CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey); return AddStorageEntry(contextKey, aURI, aIdExtension, - aStorage->WriteToDisk(), aCreateIfNotExist, aReplace, + aStorage->WriteToDisk(), aStorage->SkipSizeCheck(), + aCreateIfNotExist, aReplace, aResult); } nsresult CacheStorageService::AddStorageEntry(nsCSubstring const& aContextKey, nsIURI* aURI, const nsACString & aIdExtension, bool aWriteToDisk, + bool aSkipSizeCheck, bool aCreateIfNotExist, bool aReplace, CacheEntryHandle** aResult) { NS_ENSURE_ARG(aURI); nsresult rv; @@ -1405,17 +1425,17 @@ CacheStorageService::AddStorageEntry(nsC entry = nullptr; entryExists = false; } // Ensure entry for the particular URL, if not read/only if (!entryExists && (aCreateIfNotExist || aReplace)) { // Entry is not in the hashtable or has just been truncated... - entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk); + entry = new CacheEntry(aContextKey, aURI, aIdExtension, aWriteToDisk, aSkipSizeCheck); entries->Put(entryKey, entry); LOG((" new entry %p for %s", entry.get(), entryKey.get())); } if (entry) { // Here, if this entry was not for a long time referenced by any consumer, // gets again first 'handles count' reference. handle = entry->NewHandle();
--- a/netwerk/cache2/CacheStorageService.h +++ b/netwerk/cache2/CacheStorageService.h @@ -273,16 +273,17 @@ private: nsresult DoomStorageEntries(nsCSubstring const& aContextKey, nsILoadContextInfo* aContext, bool aDiskStorage, nsICacheEntryDoomCallback* aCallback); nsresult AddStorageEntry(nsCSubstring const& aContextKey, nsIURI* aURI, const nsACString & aIdExtension, bool aWriteToDisk, + bool aSkipSizeCheck, bool aCreateIfNotExist, bool aReplace, CacheEntryHandle** aResult); static CacheStorageService* sSelf; mozilla::Mutex mLock; mozilla::Mutex mForcedValidEntriesLock;
--- a/netwerk/cache2/nsICacheStorageService.idl +++ b/netwerk/cache2/nsICacheStorageService.idl @@ -8,17 +8,17 @@ interface nsICacheStorage; interface nsILoadContextInfo; interface nsIApplicationCache; interface nsIEventTarget; interface nsICacheStorageConsumptionObserver; /** * Provides access to particual cache storages of the network URI cache. */ -[scriptable, uuid(44de2fa4-1b0e-4cd3-9e32-211e936f721e)] +[scriptable, uuid(9c9dc1d6-533e-4716-9ad8-11e08c3763b3)] interface nsICacheStorageService : nsISupports { /** * Get storage where entries will only remain in memory, never written * to the disk. * * NOTE: Any existing disk entry for [URL|id-extension] will be doomed * prior opening an entry using this memory-only storage. Result of @@ -51,16 +51,23 @@ interface nsICacheStorageService : nsISu * @param aApplicationCache * Optional reference to an existing appcache. When left null, this will * work with offline cache as a whole. */ nsICacheStorage appCacheStorage(in nsILoadContextInfo aLoadContextInfo, in nsIApplicationCache aApplicationCache); /** + * Get storage for synthesized cache entries that we currently use for ServiceWorker interception in non-e10s mode. + * + * This cache storage has no limits on file size to allow the ServiceWorker to intercept large files. + */ + nsICacheStorage synthesizedCacheStorage(in nsILoadContextInfo aLoadContextInfo); + + /** * Evict the whole cache. */ void clear(); /** * Purge only data of disk backed entries. Metadata are left for * performance purposes. */
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp +++ b/netwerk/protocol/ftp/FTPChannelParent.cpp @@ -50,16 +50,18 @@ FTPChannelParent::FTPChannelParent(const CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler); MOZ_ASSERT(handler, "no ftp handler"); if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) { mTabParent = static_cast<dom::TabParent*>(aIframeEmbedding.get_PBrowserParent()); } mObserver = new OfflineObserver(this); + + mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this)); } FTPChannelParent::~FTPChannelParent() { gFtpHandler->Release(); if (mObserver) { mObserver->RemoveObserver(); } @@ -238,16 +240,42 @@ FTPChannelParent::RecvSuspend() bool FTPChannelParent::RecvResume() { if (mChannel) mChannel->Resume(); return true; } +class FTPDivertDataAvailableEvent : public ChannelEvent +{ +public: + FTPDivertDataAvailableEvent(FTPChannelParent* aParent, + const nsCString& data, + const uint64_t& offset, + const uint32_t& count) + : mParent(aParent) + , mData(data) + , mOffset(offset) + , mCount(count) + { + } + + void Run() + { + mParent->DivertOnDataAvailable(mData, mOffset, mCount); + } + +private: + FTPChannelParent* mParent; + nsCString mData; + uint64_t mOffset; + uint32_t mCount; +}; + bool FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data, const uint64_t& offset, const uint32_t& count) { if (NS_WARN_IF(!mDivertingFromChild)) { MOZ_ASSERT(mDivertingFromChild, "Cannot RecvDivertOnDataAvailable if diverting is not set!"); @@ -255,81 +283,185 @@ FTPChannelParent::RecvDivertOnDataAvaila return false; } // Drop OnDataAvailables if the parent was canceled already. if (NS_FAILED(mStatus)) { return true; } + if (mEventQ->ShouldEnqueue()) { + mEventQ->Enqueue(new FTPDivertDataAvailableEvent(this, data, offset, + count)); + return true; + } + + DivertOnDataAvailable(data, offset, count); + return true; +} + +void +FTPChannelParent::DivertOnDataAvailable(const nsCString& data, + const uint64_t& offset, + const uint32_t& count) +{ + LOG(("FTPChannelParent::DivertOnDataAvailable [this=%p]\n", this)); + + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot DivertOnDataAvailable if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } + + // Drop OnDataAvailables if the parent was canceled already. + if (NS_FAILED(mStatus)) { + return; + } + nsCOMPtr<nsIInputStream> stringStream; nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), count, NS_ASSIGNMENT_DEPEND); if (NS_FAILED(rv)) { if (mChannel) { mChannel->Cancel(rv); } mStatus = rv; - return true; + return; } + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count); stringStream->Close(); if (NS_FAILED(rv)) { if (mChannel) { mChannel->Cancel(rv); } mStatus = rv; } - return true; } +class FTPDivertStopRequestEvent : public ChannelEvent +{ +public: + FTPDivertStopRequestEvent(FTPChannelParent* aParent, + const nsresult& statusCode) + : mParent(aParent) + , mStatusCode(statusCode) + { + } + + void Run() { + mParent->DivertOnStopRequest(mStatusCode); + } + +private: + FTPChannelParent* mParent; + nsresult mStatusCode; +}; + bool FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode) { if (NS_WARN_IF(!mDivertingFromChild)) { MOZ_ASSERT(mDivertingFromChild, "Cannot RecvDivertOnStopRequest if diverting is not set!"); FailDiversion(NS_ERROR_UNEXPECTED); return false; } + if (mEventQ->ShouldEnqueue()) { + mEventQ->Enqueue(new FTPDivertStopRequestEvent(this, statusCode)); + return true; + } + + DivertOnStopRequest(statusCode); + return true; +} + +void +FTPChannelParent::DivertOnStopRequest(const nsresult& statusCode) +{ + LOG(("FTPChannelParent::DivertOnStopRequest [this=%p]\n", this)); + + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot DivertOnStopRequest if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } + // Honor the channel's status even if the underlying transaction completed. nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode; // Reset fake pending status in case OnStopRequest has already been called. if (mChannel) { nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel); if (forcePendingIChan) { forcePendingIChan->ForcePending(false); } } + AutoEventEnqueuer ensureSerialDispatch(mEventQ); OnStopRequest(mChannel, nullptr, status); - return true; } +class FTPDivertCompleteEvent : public ChannelEvent +{ +public: + explicit FTPDivertCompleteEvent(FTPChannelParent* aParent) + : mParent(aParent) + { + } + + void Run() { + mParent->DivertComplete(); + } + +private: + FTPChannelParent* mParent; +}; + bool FTPChannelParent::RecvDivertComplete() { if (NS_WARN_IF(!mDivertingFromChild)) { MOZ_ASSERT(mDivertingFromChild, "Cannot RecvDivertComplete if diverting is not set!");