Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2017-08-07 13:07:13 +02:00
commit 0c5d14bebd
35 changed files with 362 additions and 138 deletions

View File

@ -6410,7 +6410,7 @@
if (this._lastNumPinned != numPinned) {
this._lastNumPinned = numPinned;
this._handleTabSelect(false);
this._handleTabSelect(true);
}
]]></body>
</method>
@ -6452,7 +6452,7 @@
let tabScreenX = draggedTab.boxObject.screenX;
let translateX = screenX - draggedTab._dragData.screenX;
if (!pinned)
translateX += this.mTabstrip.scrollPosition - draggedTab._dragData.scrollX;
translateX += this.mTabstrip._scrollbox.scrollLeft - draggedTab._dragData.scrollX;
let leftBound = leftTab.boxObject.screenX - tabScreenX;
let rightBound = (rightTab.boxObject.screenX + rightTab.boxObject.width) -
(tabScreenX + tabWidth);
@ -6547,7 +6547,7 @@
var width = this.mTabstrip.boxObject.width;
if (width != this.mTabstripWidth) {
this.adjustTabstrip();
this._handleTabSelect(false);
this._handleTabSelect(true);
this.mTabstripWidth = width;
this.updateSessionRestoreVisibility();
}
@ -6996,7 +6996,7 @@
tab._dragData = {
offsetX: event.screenX - window.screenX - tabOffsetX,
offsetY: event.screenY - window.screenY,
scrollX: this.mTabstrip.scrollPosition,
scrollX: this.mTabstrip._scrollbox.scrollLeft,
screenX: event.screenX
};

View File

@ -83,6 +83,10 @@ const clearHistory = options => {
return sanitizer.items.history.clear(makeRange(options));
};
const clearLocalStorage = async function(options) {
Services.obs.notifyObservers(null, "extension:purge-localStorage");
};
const clearPasswords = async function(options) {
let loginManager = Services.logins;
let yieldCounter = 0;
@ -152,6 +156,9 @@ const doRemoval = (options, dataToRemove, extension) => {
case "history":
removalPromises.push(clearHistory(options));
break;
case "localStorage":
removalPromises.push(clearLocalStorage(options));
break;
case "passwords":
removalPromises.push(clearPasswords(options));
break;
@ -225,6 +232,9 @@ this.browsingData = class extends ExtensionAPI {
removeHistory(options) {
return doRemoval(options, {history: true});
},
removeLocalStorage(options) {
return doRemoval(options, {localStorage: true});
},
removePasswords(options) {
return doRemoval(options, {passwords: true});
},

View File

@ -341,7 +341,6 @@
"description": "Clears websites' local storage data.",
"type": "function",
"async": "callback",
"unsupported": true,
"parameters": [
{
"$ref": "RemovalOptions",

View File

@ -46,6 +46,7 @@ skip-if = (os == 'win' && !debug) # bug 1352668
[browser_ext_browserAction_theme_icons.js]
[browser_ext_browsingData_formData.js]
[browser_ext_browsingData_history.js]
[browser_ext_browsingData_localStorage.js]
[browser_ext_browsingData_pluginData.js]
[browser_ext_browsingData_serviceWorkers.js]
[browser_ext_commands_execute_browser_action.js]

View File

@ -0,0 +1,93 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
add_task(async function testLocalStorage() {
async function background() {
function openTabs() {
let promise = new Promise(resolve => {
let tabURLs = [
"http://example.com/",
"http://example.net/",
];
let tabs;
let waitingCount = tabURLs.length;
let listener = async msg => {
if (msg !== "content-script-ready" || --waitingCount) {
return;
}
browser.runtime.onMessage.removeListener(listener);
resolve(Promise.all(tabs));
};
browser.runtime.onMessage.addListener(listener);
tabs = tabURLs.map(url => {
return browser.tabs.create({url: url});
});
});
return promise;
}
function sendMessageToTabs(tabs, message) {
return Promise.all(
tabs.map(tab => { return browser.tabs.sendMessage(tab.id, message); }));
}
let tabs = await openTabs();
await sendMessageToTabs(tabs, "resetLocalStorage");
await sendMessageToTabs(tabs, "checkLocalStorageSet");
await browser.browsingData.removeLocalStorage({});
await sendMessageToTabs(tabs, "checkLocalStorageCleared");
await sendMessageToTabs(tabs, "resetLocalStorage");
await sendMessageToTabs(tabs, "checkLocalStorageSet");
await browser.browsingData.remove({}, {localStorage: true});
await sendMessageToTabs(tabs, "checkLocalStorageCleared");
browser.tabs.remove(tabs.map(tab => tab.id));
browser.test.notifyPass("done");
}
function contentScript() {
browser.runtime.onMessage.addListener(msg => {
if (msg === "resetLocalStorage") {
localStorage.clear();
localStorage.setItem("test", "test");
} else if (msg === "checkLocalStorageSet") {
browser.test.assertEq("test", localStorage.getItem("test"));
} else if (msg === "checkLocalStorageCleared") {
browser.test.assertEq(null, localStorage.getItem("test"));
}
});
browser.runtime.sendMessage("content-script-ready");
}
let extension = ExtensionTestUtils.loadExtension({
background,
manifest: {
"permissions": ["browsingData"],
"content_scripts": [{
"matches": [
"http://example.com/",
"http://example.net/",
],
"js": ["content-script.js"],
"run_at": "document_start",
}],
},
files: {
"content-script.js": contentScript,
},
});
await extension.startup();
await extension.awaitFinish("done");
await extension.unload();
});

View File

@ -44,7 +44,7 @@ add_task(async function testInvalidArguments() {
add_task(async function testUnimplementedDataType() {
function background() {
browser.browsingData.remove({}, {localStorage: true});
browser.browsingData.remove({}, {indexedDB: true});
browser.test.sendMessage("finished");
}
@ -61,6 +61,6 @@ add_task(async function testUnimplementedDataType() {
await extension.unload();
});
let warningObserved = messages.find(line => /Firefox does not support dataTypes: localStorage/.test(line));
let warningObserved = messages.find(line => /Firefox does not support dataTypes: indexedDB/.test(line));
ok(warningObserved, "Warning issued when calling remove with an unimplemented dataType.");
});

View File

@ -13,6 +13,7 @@ const CACHED_STYLESHEETS = new WeakMap();
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillParent",
"resource://formautofill/FormAutofillParent.jsm");
@ -40,12 +41,21 @@ function onMaybeOpenPopup(evt) {
insertStyleSheet(domWindow, STYLESHEET_URI);
}
function startup() {
function startup(data) {
if (Services.prefs.getStringPref("extensions.formautofill.available") != "on") {
Services.prefs.clearUserPref("dom.forms.autocomplete.formautofill");
return;
}
if (data.hasOwnProperty("instanceID") && data.instanceID) {
AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
// don't install the upgrade by doing nothing here.
// The upgrade will be installed upon next restart.
});
} else {
throw Error("no instanceID passed to bootstrap startup");
}
// This pref is used for web contents to detect the autocomplete feature.
// When it's true, "element.autocomplete" will return tokens we currently
// support -- otherwise it'll return an empty string.

View File

@ -13,20 +13,31 @@ browser.onboarding.tour-set = "new" // for new user tour, or "update" for update
```
And make sure the value of `browser.onboarding.tourset-verion` and `browser.onboarding.seen-tourset-verion` are the same.
## How to show the onboarding notification
Besides above settings, notification will wait 5 minutes before showing the first notification on a new profile or the updated user profile (to not put too much information to the user at once).
To manually remove the mute duration, set pref `browser.onboarding.notification.mute-duration-on-first-session-ms` to `0` and notification will be shown at the next time you open `about:home` or `about:newtab`.
## How to show the snippets
Snippets (the remote notification that handled by activity stream) will only be shown after onboarding notifications are all done. You can set preference `browser.onboarding.notification.finished` to `true` to disable onboarding notification and accept snippets right away.
## Architecture
Everytime `about:home` or `about:newtab` page is opened, onboarding overlay is injected into that page.
`OnboardingTourType.jsm` will check the onboarding tour type (currently support `new` and `update`). Then in `onboarding.js`, All tours are defined inside of `onboardingTourset` dictionary. `getTourIDList` function will load tours from proper preferences. (Check `How to change the order of tours` section for more detail).
When user clicks the action button in each tour, We use [UITour](http://bedrock.readthedocs.io/en/latest/uitour.html) to highlight the correspondent browser UI element.
When user clicks the action button in each tour, We use [UITour](http://bedrock.readthedocs.io/en/latest/uitour.html) to highlight the correspondent browser UI element. The UITour client is bundled in onboarding addon via `jar.mn`.
## Landing rules
We would apply some rules:
* All styles and ids should be formatted as `onboarding-*` to avoid conflict with the origin page.
* All strings in `locales` should be formatted as `onboarding.*` for consistency.
* To avoid conflict with the origin page, all styles and ids should be formatted as `onboarding-*`.
* For consistency and easier filtering, all strings in `locales` should be formatted as `onboarding.*`.
* For consistency, all related preferences should be formatted as `browser.onboarding.*`.
* For accessibility, images that are for presentation only should have `role="presentation"` attribute.
## How to change the order of tours

View File

@ -85,7 +85,7 @@ function test_getUnanimatedComputedStyle() {
}
addStyle([cssAnimationStyle,
".pseudo::before { animation: cssanimation 1s; }"]);
".pseudo::before { animation: cssanimation 1s; content: ''}"]);
const pseudoAnimation = target => {
target.classList.add("pseudo");
return target.getAnimations({ subtree: true })[0];

View File

@ -3744,9 +3744,11 @@ HTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI)
// Ditto for anything else that could cause us to send different headers.
if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal &&
elem->mCORSMode == mCORSMode) {
NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetResource(), "Decoder gone");
MediaResource* resource = elem->mDecoder->GetResource();
if (resource->CanClone()) {
// See SetupDecoder() below. We only add a element to the table when
// mDecoder is a ChannelMediaDecoder.
auto decoder = static_cast<ChannelMediaDecoder*>(elem->mDecoder.get());
NS_ASSERTION(decoder, "Decoder gone");
if (decoder->CanClone()) {
return elem;
}
}
@ -4721,10 +4723,6 @@ HTMLMediaElement::InitializeDecoderAsClone(ChannelMediaDecoder* aOriginal)
NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
MediaResource* originalResource = aOriginal->GetResource();
if (!originalResource)
return NS_ERROR_FAILURE;
MediaDecoderInit decoderInit(this,
mAudioChannel,
mMuted ? 0.0 : mVolume,
@ -4742,14 +4740,6 @@ HTMLMediaElement::InitializeDecoderAsClone(ChannelMediaDecoder* aOriginal)
LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
nsresult rv = decoder->Load(originalResource);
if (NS_FAILED(rv)) {
decoder->Shutdown();
LOG(LogLevel::Debug,
("%p Failed to load for decoder %p", this, decoder.get()));
return rv;
}
return FinishDecoderSetup(decoder);
}

View File

@ -12,7 +12,7 @@
namespace mozilla {
ChannelMediaDecoder*
ADTSDecoder::Clone(MediaDecoderInit& aInit)
ADTSDecoder::CloneImpl(MediaDecoderInit& aInit)
{
if (!IsEnabled())
return nullptr;

View File

@ -21,12 +21,14 @@ public:
: ChannelMediaDecoder(aInit)
{
}
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override;
// Returns true if the ADTS backend is pref'ed on, and we're running on a
// platform that is likely to have decoders for the format.
static bool IsEnabled();
static bool IsSupportedType(const MediaContainerType& aContainerType);
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override;
};
} // namespace mozilla

View File

@ -155,6 +155,31 @@ ChannelMediaDecoder::ChannelMediaDecoder(MediaDecoderInit& aInit)
mResourceCallback->Connect(this);
}
bool
ChannelMediaDecoder::CanClone()
{
MOZ_ASSERT(NS_IsMainThread());
return mResource && mResource->CanClone();
}
already_AddRefed<ChannelMediaDecoder>
ChannelMediaDecoder::Clone(MediaDecoderInit& aInit)
{
if (!mResource) {
return nullptr;
}
RefPtr<ChannelMediaDecoder> decoder = CloneImpl(aInit);
if (!decoder) {
return nullptr;
}
nsresult rv = decoder->Load(mResource);
if (NS_FAILED(rv)) {
decoder->Shutdown();
return nullptr;
}
return decoder.forget();
}
MediaResource*
ChannelMediaDecoder::GetResource() const
{
@ -200,7 +225,7 @@ ChannelMediaDecoder::Load(nsIChannel* aChannel,
MOZ_ASSERT(!mResource);
mResource =
MediaResource::Create(mResourceCallback, aChannel, aIsPrivateBrowsing);
BaseMediaResource::Create(mResourceCallback, aChannel, aIsPrivateBrowsing);
if (!mResource) {
return NS_ERROR_FAILURE;
}
@ -220,7 +245,7 @@ ChannelMediaDecoder::Load(nsIChannel* aChannel,
}
nsresult
ChannelMediaDecoder::Load(MediaResource* aOriginal)
ChannelMediaDecoder::Load(BaseMediaResource* aOriginal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mResource);

View File

@ -15,6 +15,8 @@ class nsIStreamListener;
namespace mozilla {
class BaseMediaResource;
class ChannelMediaDecoder : public MediaDecoder
{
// Used to register with MediaResource to receive notifications which will
@ -53,7 +55,7 @@ class ChannelMediaDecoder : public MediaDecoder
protected:
RefPtr<ResourceCallback> mResourceCallback;
RefPtr<MediaResource> mResource;
RefPtr<BaseMediaResource> mResource;
public:
explicit ChannelMediaDecoder(MediaDecoderInit& aInit);
@ -64,17 +66,19 @@ public:
void Shutdown() override;
// Create a new decoder of the same type as this one.
// Subclasses must implement this.
virtual ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) = 0;
bool CanClone();
virtual nsresult Load(nsIChannel* aChannel,
bool aIsPrivateBrowsing,
nsIStreamListener** aStreamListener);
virtual nsresult Load(MediaResource* aOriginal);
// Create a new decoder of the same type as this one.
already_AddRefed<ChannelMediaDecoder> Clone(MediaDecoderInit& aInit);
nsresult Load(nsIChannel* aChannel,
bool aIsPrivateBrowsing,
nsIStreamListener** aStreamListener);
private:
virtual ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) = 0;
nsresult OpenResource(nsIStreamListener** aStreamListener);
nsresult Load(BaseMediaResource* aOriginal);
};
} // namespace mozilla

View File

@ -642,7 +642,8 @@ bool ChannelMediaResource::CanClone()
return mCacheStream.IsAvailableForSharing();
}
already_AddRefed<MediaResource> ChannelMediaResource::CloneData(MediaResourceCallback* aCallback)
already_AddRefed<BaseMediaResource>
ChannelMediaResource::CloneData(MediaResourceCallback* aCallback)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
NS_ASSERTION(mCacheStream.IsAvailableForSharing(), "Stream can't be cloned");
@ -1413,9 +1414,10 @@ int64_t FileMediaResource::Tell()
return offset;
}
already_AddRefed<MediaResource>
MediaResource::Create(MediaResourceCallback* aCallback,
nsIChannel* aChannel, bool aIsPrivateBrowsing)
already_AddRefed<BaseMediaResource>
BaseMediaResource::Create(MediaResourceCallback* aCallback,
nsIChannel* aChannel,
bool aIsPrivateBrowsing)
{
NS_ASSERTION(NS_IsMainThread(),
"MediaResource::Open called on non-main thread");
@ -1434,7 +1436,7 @@ MediaResource::Create(MediaResourceCallback* aCallback,
return nullptr;
}
RefPtr<MediaResource> resource;
RefPtr<BaseMediaResource> resource;
// Let's try to create a FileMediaResource in case the channel is a nsIFile
nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);

View File

@ -174,20 +174,6 @@ public:
virtual void Resume() = 0;
// Get the current principal for the channel
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
// If this returns false, then we shouldn't try to clone this MediaResource
// because its underlying resources are not suitable for reuse (e.g.
// because the underlying connection has been lost, or this resource
// just can't be safely cloned). If this returns true, CloneData could
// still fail. If this returns false, CloneData should not be called.
virtual bool CanClone() { return false; }
// Create a new stream of the same type that refers to the same URI
// with a new channel. Any cached data associated with the original
// stream should be accessible in the new stream too.
virtual already_AddRefed<MediaResource> CloneData(
MediaResourceCallback* aCallback)
{
return nullptr;
}
// These methods are called off the main thread.
// The mode is initially MODE_PLAYBACK.
@ -294,14 +280,6 @@ public:
// requests are supported by the connection/server.
virtual bool IsTransportSeekable() = 0;
/**
* Create a resource, reading data from the channel. Call on main thread only.
* The caller must follow up by calling resource->Open().
*/
static already_AddRefed<MediaResource>
Create(MediaResourceCallback* aCallback,
nsIChannel* aChannel, bool aIsPrivateBrowsing);
/**
* Open the stream. This creates a stream listener and returns it in
* aStreamListener; this listener needs to be notified of incoming data.
@ -338,6 +316,31 @@ private:
class BaseMediaResource : public MediaResource {
public:
/**
* Create a resource, reading data from the channel. Call on main thread only.
* The caller must follow up by calling resource->Open().
*/
static already_AddRefed<BaseMediaResource> Create(
MediaResourceCallback* aCallback,
nsIChannel* aChannel,
bool aIsPrivateBrowsing);
// If this returns false, then we shouldn't try to clone this MediaResource
// because its underlying resources are not suitable for reuse (e.g.
// because the underlying connection has been lost, or this resource
// just can't be safely cloned). If this returns true, CloneData could
// still fail. If this returns false, CloneData should not be called.
virtual bool CanClone() { return false; }
// Create a new stream of the same type that refers to the same URI
// with a new channel. Any cached data associated with the original
// stream should be accessible in the new stream too.
virtual already_AddRefed<BaseMediaResource> CloneData(
MediaResourceCallback* aCallback)
{
return nullptr;
}
void SetLoadInBackground(bool aLoadInBackground) override;
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
@ -497,7 +500,8 @@ public:
// Return true if the stream has been closed.
bool IsClosed() const { return mCacheStream.IsClosed(); }
bool CanClone() override;
already_AddRefed<MediaResource> CloneData(MediaResourceCallback* aDecoder) override;
already_AddRefed<BaseMediaResource> CloneData(
MediaResourceCallback* aDecoder) override;
nsresult ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) override;
void EnsureCacheUpToDate() override;

View File

@ -12,7 +12,7 @@
namespace mozilla {
ChannelMediaDecoder*
FlacDecoder::Clone(MediaDecoderInit& aInit)
FlacDecoder::CloneImpl(MediaDecoderInit& aInit)
{
if (!IsEnabled()) {
return nullptr;

View File

@ -21,12 +21,14 @@ public:
: ChannelMediaDecoder(aInit)
{
}
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override;
// Returns true if the Flac backend is pref'ed on, and we're running on a
// platform that is likely to have decoders for the format.
static bool IsEnabled();
static bool IsSupportedType(const MediaContainerType& aContainerType);
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override;
};
} // namespace mozilla

View File

@ -20,14 +20,6 @@ class MP4Decoder : public ChannelMediaDecoder
public:
explicit MP4Decoder(MediaDecoderInit& aInit);
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override
{
if (!IsEnabled()) {
return nullptr;
}
return new MP4Decoder(aInit);
}
// Returns true if aContainerType is an MP4 type that we think we can render
// with the a platform decoder backend.
// If provided, codecs are checked for support.
@ -51,6 +43,15 @@ public:
IsVideoAccelerated(layers::KnowsCompositor* aKnowsCompositor, nsIGlobalObject* aParent);
void GetMozDebugReaderData(nsACString& aString) override;
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override
{
if (!IsEnabled()) {
return nullptr;
}
return new MP4Decoder(aInit);
}
};
} // namespace mozilla

View File

@ -14,7 +14,7 @@
namespace mozilla {
ChannelMediaDecoder*
MP3Decoder::Clone(MediaDecoderInit& aInit)
MP3Decoder::CloneImpl(MediaDecoderInit& aInit)
{
if (!IsEnabled()) {
return nullptr;

View File

@ -20,12 +20,14 @@ public:
: ChannelMediaDecoder(aInit)
{
}
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override;
// Returns true if the MP3 backend is preffed on, and we're running on a
// platform that is likely to have decoders for the format.
static bool IsEnabled();
static bool IsSupportedType(const MediaContainerType& aContainerType);
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override;
};
} // namespace mozilla

View File

@ -19,19 +19,21 @@ public:
: ChannelMediaDecoder(aInit)
{}
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override
{
if (!IsOggEnabled()) {
return nullptr;
}
return new OggDecoder(aInit);
}
MediaDecoderStateMachine* CreateStateMachine() override;
// Returns true if aContainerType is an Ogg type that we think we can render
// with an enabled platform decoder backend.
// If provided, codecs are checked for support.
static bool IsSupportedType(const MediaContainerType& aContainerType);
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override
{
if (!IsOggEnabled()) {
return nullptr;
}
return new OggDecoder(aInit);
}
};
} // namespace mozilla

View File

@ -12,7 +12,7 @@
namespace mozilla {
ChannelMediaDecoder*
WaveDecoder::Clone(MediaDecoderInit& aInit)
WaveDecoder::CloneImpl(MediaDecoderInit& aInit)
{
return new WaveDecoder(aInit);
}

View File

@ -20,11 +20,13 @@ public:
: ChannelMediaDecoder(aInit)
{
}
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override;
// Returns true if the Wave backend is pref'ed on, and we're running on a
// platform that is likely to have decoders for the format.
static bool IsSupportedType(const MediaContainerType& aContainerType);
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override;
};
} // namespace mozilla

View File

@ -19,13 +19,6 @@ public:
: ChannelMediaDecoder(aInit)
{
}
ChannelMediaDecoder* Clone(MediaDecoderInit& aInit) override
{
if (!IsWebMEnabled()) {
return nullptr;
}
return new WebMDecoder(aInit);
}
// Returns true if aContainerType is a WebM type that we think we can render
// with an enabled platform decoder backend.
@ -33,6 +26,15 @@ public:
static bool IsSupportedType(const MediaContainerType& aContainerType);
void GetMozDebugReaderData(nsACString& aString) override;
private:
ChannelMediaDecoder* CloneImpl(MediaDecoderInit& aInit) override
{
if (!IsWebMEnabled()) {
return nullptr;
}
return new WebMDecoder(aInit);
}
};
} // namespace mozilla

View File

@ -386,7 +386,8 @@ LocalStorageManager::Observe(const char* aTopic,
}
// Clear everything, caches + database
if (!strcmp(aTopic, "cookie-cleared")) {
if (!strcmp(aTopic, "cookie-cleared") ||
!strcmp(aTopic, "extension:purge-localStorage-caches")) {
ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
return NS_OK;
}

View File

@ -66,6 +66,7 @@ StorageObserver::Init()
obs->AddObserver(sSelf, "browser:purge-domain-data", true);
obs->AddObserver(sSelf, "last-pb-context-exited", true);
obs->AddObserver(sSelf, "clear-origin-attributes-data", true);
obs->AddObserver(sSelf, "extension:purge-localStorage", true);
// Shutdown
obs->AddObserver(sSelf, "profile-after-change", true);
@ -252,6 +253,17 @@ StorageObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
if (!strcmp(aTopic, "extension:purge-localStorage")) {
StorageDBBridge* db = LocalStorageCache::StartDatabase();
NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
db->AsyncClearAll();
Notify("extension:purge-localStorage-caches");
return NS_OK;
}
// Clear everything (including so and pb data) from caches and database
// for the gived domain and subdomains.
if (!strcmp(aTopic, "browser:purge-domain-data")) {

View File

@ -239,7 +239,11 @@ HTMLEditor::CreateAnonymousElement(nsIAtom* aTag,
// Must style the new element, otherwise the PostRecreateFramesFor call
// below will do nothing.
if (ServoStyleSet* styleSet = ps->StyleSet()->GetAsServo()) {
styleSet->StyleNewSubtree(newContent);
// Sometimes editor likes to append anonymous content to elements
// in display:none subtrees, so avoid styling in those cases.
if (styleSet->MayTraverseFrom(newContent)) {
styleSet->StyleNewSubtree(newContent);
}
}
ElementDeletionObserver* observer =

View File

@ -828,8 +828,30 @@ nsFrame::GetOffsets(int32_t &aStart, int32_t &aEnd) const
return NS_OK;
}
static
void
static void
CompareLayers(const nsStyleImageLayers* aFirstLayers,
const nsStyleImageLayers* aSecondLayers,
const std::function<void(imgRequestProxy* aReq)>& aCallback)
{
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
const nsStyleImage& image = aFirstLayers->mLayers[i].mImage;
if (image.GetType() != eStyleImageType_Image || !image.IsResolved()) {
continue;
}
// aCallback is called when the style image in aFirstLayers is thought to
// be different with the corresponded one in aSecondLayers
if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
(!aSecondLayers->mLayers[i].mImage.IsResolved() ||
!image.ImageDataEquals(aSecondLayers->mLayers[i].mImage))) {
if (imgRequestProxy* req = image.GetImageData()) {
aCallback(req);
}
}
}
}
static void
AddAndRemoveImageAssociations(nsFrame* aFrame,
const nsStyleImageLayers* aOldLayers,
const nsStyleImageLayers* aNewLayers)
@ -845,43 +867,17 @@ AddAndRemoveImageAssociations(nsFrame* aFrame,
// to clear those notifiers unless we have to. (They'll be reset
// when we paint, although we could miss a notification in that
// interval.)
if (aOldLayers) {
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aOldLayers)) {
const nsStyleImage& oldImage = aOldLayers->mLayers[i].mImage;
if (oldImage.GetType() != eStyleImageType_Image ||
!oldImage.IsResolved()) {
continue;
}
// If there is an image in oldBG that's not in newBG, drop it.
if (i >= aNewLayers->mImageCount ||
!oldImage.ImageDataEquals(aNewLayers->mLayers[i].mImage)) {
if (aFrame->HasImageRequest()) {
if (imgRequestProxy* req = oldImage.GetImageData()) {
imageLoader->DisassociateRequestFromFrame(req, aFrame);
}
}
}
}
CompareLayers(aOldLayers, aNewLayers,
[&imageLoader, aFrame](imgRequestProxy* aReq)
{ imageLoader->DisassociateRequestFromFrame(aReq, aFrame); }
);
}
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aNewLayers)) {
const nsStyleImage& newImage = aNewLayers->mLayers[i].mImage;
if (newImage.GetType() != eStyleImageType_Image ||
!newImage.IsResolved()) {
continue;
}
// If there is an image in newBG that's not in oldBG, add it.
if (!aOldLayers || i >= aOldLayers->mImageCount ||
!newImage.ImageDataEquals(aOldLayers->mLayers[i].mImage)) {
if (imgRequestProxy* req = newImage.GetImageData()) {
imageLoader->AssociateRequestToFrame(req, aFrame);
}
}
}
CompareLayers(aNewLayers, aOldLayers,
[&imageLoader, aFrame](imgRequestProxy* aReq)
{ imageLoader->AssociateRequestToFrame(aReq, aFrame); }
);
}
// Subclass hook for style post processing

View File

@ -0,0 +1,26 @@
<html>
<head>
<script id='x'>
try { o1 = document.getElementById('x') } catch(e) { }
try { o2 = document.createElement('table') } catch(e) { }
try { o3 = document.createElement('td') } catch(e) { }
try { o5 = document.createElement('input') } catch(e) { }
try { o6 = document.createTextNode('{\r\u2044/=+=\u06F0\u2029\u06F9a \uDC1D0\n-\v') } catch(e) { }
try { o7 = new Image(0.167398293512524, 0.2503646329685738) } catch(e) { }
try { o1.appendChild(o2) } catch(e) { }
try { o2.appendChild(o3) } catch(e) { }
try { o3.appendChild(o6) } catch(e) { }
try { o8 = document.createRange() } catch(e) { }
try { document.documentElement.appendChild(o7) } catch(e) { }
try { o7.outerHTML = '<select contenteditable="true"></select>' } catch(e) { }
try { document.documentElement.appendChild(o5) } catch(e) { }
try { o9 = window.getSelection() } catch(e) { }
try { o5.select() } catch(e) { }
try { document.replaceChild(document.documentElement, document.documentElement) } catch(e) { }
try { document.designMode = 'on' } catch(e) { }
try { o8.selectNode(o6) } catch(e) { }
try { o9.addRange(o8) } catch(e) { }
try { document.execCommand('unlink', false, null) } catch(e) { }
</script>
</head>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html class="reftest-wait">
<iframe id="f" src="1387481-1-iframe.html" onload="step()"></iframe>
<script>
var reloads = 3;
function step() {
if (--reloads) {
f.contentWindow.location.reload();
} else {
document.documentElement.className = "";
}
}
</script>

View File

@ -198,3 +198,4 @@ load 1383981.html
load 1383981-2.html
load 1384824-1.html
load 1384824-2.html
load 1387481-1.html

View File

@ -345,7 +345,6 @@
"description": "Clears websites' local storage data.",
"type": "function",
"async": "callback",
"unsupported": true,
"parameters": [
{
"$ref": "RemovalOptions",

View File

@ -5711,11 +5711,7 @@ pref("dom.webkitBlink.dirPicker.enabled", true);
pref("dom.webkitBlink.filesystem.enabled", true);
#endif
#ifdef RELEASE_OR_BETA
pref("media.block-autoplay-until-in-foreground", false);
#else
pref("media.block-autoplay-until-in-foreground", true);
#endif
// Is Stylo CSS support built and enabled?
// Only define this pref if Stylo support is actually built in.

View File

@ -622,6 +622,20 @@ GetWidget(WidgetNodeType aWidgetType)
GtkWidget* widget = sWidgetStorage[aWidgetType];
if (!widget) {
widget = CreateWidget(aWidgetType);
// In GTK versions prior to 3.18, automatic invalidation of style contexts
// for widgets was delayed until the next resize event. Gecko however,
// typically uses the style context before the resize event runs and so an
// explicit invalidation may be required. This is necessary if a style
// property was retrieved before all changes were made to the style
// context. One such situation is where gtk_button_construct_child()
// retrieves the style property "image-spacing" during construction of the
// GtkButton, before its parent is set to provide inheritance of ancestor
// properties. More recent GTK versions do not need this, but do not
// re-resolve until required and so invalidation does not trigger
// unnecessary resolution in general.
GtkStyleContext* style = gtk_widget_get_style_context(widget);
gtk_style_context_invalidate(style);
sWidgetStorage[aWidgetType] = widget;
}
return widget;