зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
c2716211ca
|
@ -14,6 +14,7 @@ support-files =
|
|||
support-files =
|
||||
browser_autoplay_blocked.html
|
||||
../general/audio.ogg
|
||||
skip-if = verify && os == 'linux' && debug # Bug 1483648
|
||||
[browser_autoplay_doorhanger.js]
|
||||
support-files =
|
||||
browser_autoplay_blocked.html
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
|
||||
const Services = require("Services");
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const defer = require("devtools/shared/defer");
|
||||
const {LongStringActor} = require("devtools/server/actors/string");
|
||||
const {DebuggerServer} = require("devtools/server/main");
|
||||
const {getSystemInfo, getSetting} = require("devtools/shared/system");
|
||||
const {getSystemInfo} = require("devtools/shared/system");
|
||||
const {deviceSpec} = require("devtools/shared/specs/device");
|
||||
const FileReader = require("FileReader");
|
||||
|
||||
exports.DeviceActor = protocol.ActorClassWithSpec(deviceSpec, {
|
||||
_desc: null,
|
||||
|
@ -20,23 +18,6 @@ exports.DeviceActor = protocol.ActorClassWithSpec(deviceSpec, {
|
|||
return getSystemInfo();
|
||||
},
|
||||
|
||||
getWallpaper: function() {
|
||||
const deferred = defer();
|
||||
getSetting("wallpaper.image").then((blob) => {
|
||||
const reader = new FileReader();
|
||||
const conn = this.conn;
|
||||
reader.addEventListener("load", function() {
|
||||
const str = new LongStringActor(conn, reader.result);
|
||||
deferred.resolve(str);
|
||||
});
|
||||
reader.addEventListener("error", function() {
|
||||
deferred.reject(reader.error);
|
||||
});
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
screenshotToDataURL: function() {
|
||||
const window = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
|
||||
const { devicePixelRatio } = window;
|
||||
|
|
|
@ -10,7 +10,6 @@ const deviceSpec = generateActorSpec({
|
|||
|
||||
methods: {
|
||||
getDescription: {request: {}, response: { value: RetVal("json")}},
|
||||
getWallpaper: {request: {}, response: { value: RetVal("longstring")}},
|
||||
screenshotToDataURL: {request: {}, response: { value: RetVal("longstring")}},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
const { Cc, Ci } = require("chrome");
|
||||
|
||||
loader.lazyRequireGetter(this, "Services");
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
|
||||
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
|
||||
loader.lazyRequireGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm", true);
|
||||
|
@ -239,30 +237,5 @@ function getScreenDimensions() {
|
|||
return 11;
|
||||
}
|
||||
|
||||
function getSetting(name) {
|
||||
const deferred = defer();
|
||||
|
||||
if ("@mozilla.org/settingsService;1" in Cc) {
|
||||
let settingsService;
|
||||
|
||||
// TODO bug 1205797, make this work in child processes.
|
||||
try {
|
||||
settingsService = Cc["@mozilla.org/settingsService;1"]
|
||||
.getService(Ci.nsISettingsService);
|
||||
} catch (e) {
|
||||
return promise.reject(e);
|
||||
}
|
||||
|
||||
settingsService.createLock().get(name, {
|
||||
handle: (_, value) => deferred.resolve(value),
|
||||
handleError: (error) => deferred.reject(error),
|
||||
});
|
||||
} else {
|
||||
deferred.reject(new Error("No settings service"));
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
exports.getSystemInfo = getSystemInfo;
|
||||
exports.getSetting = getSetting;
|
||||
exports.getScreenDimensions = getScreenDimensions;
|
||||
|
|
|
@ -9055,33 +9055,30 @@ nsDocShell::CopyFavicon(nsIURI* aOldURI,
|
|||
#endif
|
||||
}
|
||||
|
||||
struct InternalLoadData
|
||||
class InternalLoadEvent : public Runnable
|
||||
{
|
||||
public:
|
||||
InternalLoadData(nsDocShell* aDocShell,
|
||||
nsIURI* aURI,
|
||||
nsIURI* aOriginalURI,
|
||||
Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
|
||||
bool aKeepResultPrincipalURIIfSet,
|
||||
bool aLoadReplace,
|
||||
nsIURI* aReferrer, uint32_t aReferrerPolicy,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aPrincipalToInherit,
|
||||
uint32_t aFlags,
|
||||
const nsAString& aWindowTarget,
|
||||
const char* aTypeHint,
|
||||
const nsAString& aFileName,
|
||||
nsIInputStream* aPostData,
|
||||
nsIInputStream* aHeadersData,
|
||||
uint32_t aLoadType,
|
||||
nsISHEntry* aSHEntry,
|
||||
bool aFirstParty,
|
||||
const nsAString& aSrcdoc,
|
||||
nsIDocShell* aSourceDocShell,
|
||||
nsIURI* aBaseURI,
|
||||
nsIDocShell** aDocShell2,
|
||||
nsIRequest** aRequest)
|
||||
: mSrcdoc(aSrcdoc)
|
||||
InternalLoadEvent(nsDocShell* aDocShell,
|
||||
nsIURI* aURI,
|
||||
nsIURI* aOriginalURI,
|
||||
Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
|
||||
bool aKeepResultPrincipalURIIfSet,
|
||||
bool aLoadReplace,
|
||||
nsIURI* aReferrer, uint32_t aReferrerPolicy,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aPrincipalToInherit,
|
||||
uint32_t aFlags,
|
||||
const char* aTypeHint,
|
||||
nsIInputStream* aPostData,
|
||||
nsIInputStream* aHeadersData,
|
||||
uint32_t aLoadType,
|
||||
nsISHEntry* aSHEntry,
|
||||
bool aFirstParty,
|
||||
const nsAString& aSrcdoc,
|
||||
nsIDocShell* aSourceDocShell,
|
||||
nsIURI* aBaseURI)
|
||||
: mozilla::Runnable("InternalLoadEvent")
|
||||
, mSrcdoc(aSrcdoc)
|
||||
, mDocShell(aDocShell)
|
||||
, mURI(aURI)
|
||||
, mOriginalURI(aOriginalURI)
|
||||
|
@ -9096,14 +9093,10 @@ public:
|
|||
, mHeadersData(aHeadersData)
|
||||
, mSHEntry(aSHEntry)
|
||||
, mFlags(aFlags)
|
||||
, mWindowTarget(aWindowTarget)
|
||||
, mFileName(aFileName)
|
||||
, mLoadType(aLoadType)
|
||||
, mFirstParty(aFirstParty)
|
||||
, mSourceDocShell(aSourceDocShell)
|
||||
, mBaseURI(aBaseURI)
|
||||
, mDocShell2(aDocShell2)
|
||||
, mRequest(aRequest)
|
||||
{
|
||||
// Make sure to keep null things null as needed
|
||||
if (aTypeHint) {
|
||||
|
@ -9113,7 +9106,8 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
nsresult Run()
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
return mDocShell->InternalLoad(mURI, mOriginalURI, mResultPrincipalURI,
|
||||
mKeepResultPrincipalURIIfSet,
|
||||
|
@ -9121,16 +9115,17 @@ public:
|
|||
mReferrer,
|
||||
mReferrerPolicy,
|
||||
mTriggeringPrincipal, mPrincipalToInherit,
|
||||
mFlags, mWindowTarget,
|
||||
mFlags, EmptyString(),
|
||||
mTypeHint.IsVoid() ? nullptr
|
||||
: mTypeHint.get(),
|
||||
mFileName, mPostData,
|
||||
VoidString(), mPostData,
|
||||
mHeadersData, mLoadType, mSHEntry,
|
||||
mFirstParty, mSrcdoc, mSourceDocShell,
|
||||
mBaseURI, mDocShell2,
|
||||
mRequest);
|
||||
mBaseURI, nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
nsCString mTypeHint;
|
||||
nsString mSrcdoc;
|
||||
|
||||
|
@ -9148,174 +9143,12 @@ public:
|
|||
nsCOMPtr<nsIInputStream> mHeadersData;
|
||||
nsCOMPtr<nsISHEntry> mSHEntry;
|
||||
uint32_t mFlags;
|
||||
nsString mWindowTarget;
|
||||
nsString mFileName;
|
||||
uint32_t mLoadType;
|
||||
bool mFirstParty;
|
||||
nsCOMPtr<nsIDocShell> mSourceDocShell;
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
nsIDocShell** mDocShell2;
|
||||
nsIRequest** mRequest;
|
||||
};
|
||||
|
||||
class InternalLoadEvent : public Runnable
|
||||
{
|
||||
public:
|
||||
InternalLoadEvent(nsDocShell* aDocShell,
|
||||
nsIURI* aURI,
|
||||
nsIURI* aOriginalURI,
|
||||
Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
|
||||
bool aKeepResultPrincipalURIIfSet,
|
||||
bool aLoadReplace,
|
||||
nsIURI* aReferrer,
|
||||
uint32_t aReferrerPolicy,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aPrincipalToInherit,
|
||||
uint32_t aFlags,
|
||||
const char* aTypeHint,
|
||||
nsIInputStream* aPostData,
|
||||
nsIInputStream* aHeadersData,
|
||||
uint32_t aLoadType,
|
||||
nsISHEntry* aSHEntry,
|
||||
bool aFirstParty,
|
||||
const nsAString& aSrcdoc,
|
||||
nsIDocShell* aSourceDocShell,
|
||||
nsIURI* aBaseURI)
|
||||
: mozilla::Runnable("InternalLoadEvent")
|
||||
, mLoadData(aDocShell,
|
||||
aURI,
|
||||
aOriginalURI,
|
||||
aResultPrincipalURI,
|
||||
aKeepResultPrincipalURIIfSet,
|
||||
aLoadReplace,
|
||||
aReferrer,
|
||||
aReferrerPolicy,
|
||||
aTriggeringPrincipal,
|
||||
aPrincipalToInherit,
|
||||
aFlags,
|
||||
EmptyString(),
|
||||
aTypeHint,
|
||||
VoidString(),
|
||||
aPostData,
|
||||
aHeadersData,
|
||||
aLoadType,
|
||||
aSHEntry,
|
||||
aFirstParty,
|
||||
aSrcdoc,
|
||||
aSourceDocShell,
|
||||
aBaseURI,
|
||||
nullptr,
|
||||
nullptr)
|
||||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
return mLoadData.Run();
|
||||
}
|
||||
|
||||
private:
|
||||
InternalLoadData mLoadData;
|
||||
};
|
||||
|
||||
class LoadURIDelegateHandler final : public PromiseNativeHandler
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(LoadURIDelegateHandler)
|
||||
|
||||
LoadURIDelegateHandler(nsDocShell* aDocShell,
|
||||
nsIURI* aURI,
|
||||
nsIURI* aOriginalURI,
|
||||
Maybe<nsCOMPtr<nsIURI>> const& aResultPrincipalURI,
|
||||
bool aKeepResultPrincipalURIIfSet,
|
||||
bool aLoadReplace,
|
||||
nsIURI* aReferrer,
|
||||
uint32_t aReferrerPolicy,
|
||||
nsIPrincipal* aTriggeringPrincipal,
|
||||
nsIPrincipal* aPrincipalToInherit,
|
||||
uint32_t aFlags,
|
||||
const nsAString& aWindowTarget,
|
||||
const char* aTypeHint,
|
||||
const nsAString& aFileName,
|
||||
nsIInputStream* aPostData,
|
||||
nsIInputStream* aHeadersData,
|
||||
uint32_t aLoadType,
|
||||
nsISHEntry* aSHEntry,
|
||||
bool aFirstParty,
|
||||
const nsAString& aSrcdoc,
|
||||
nsIDocShell* aSourceDocShell,
|
||||
nsIURI* aBaseURI,
|
||||
nsIDocShell** aDocShell2,
|
||||
nsIRequest** aRequest)
|
||||
: mLoadData(aDocShell,
|
||||
aURI,
|
||||
aOriginalURI,
|
||||
aResultPrincipalURI,
|
||||
aKeepResultPrincipalURIIfSet,
|
||||
aLoadReplace,
|
||||
aReferrer,
|
||||
aReferrerPolicy,
|
||||
aTriggeringPrincipal,
|
||||
aPrincipalToInherit,
|
||||
aFlags,
|
||||
aWindowTarget,
|
||||
aTypeHint,
|
||||
aFileName,
|
||||
aPostData,
|
||||
aHeadersData,
|
||||
aLoadType,
|
||||
aSHEntry,
|
||||
aFirstParty,
|
||||
aSrcdoc,
|
||||
aSourceDocShell,
|
||||
aBaseURI,
|
||||
aDocShell2,
|
||||
aRequest)
|
||||
{}
|
||||
|
||||
void
|
||||
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
||||
{
|
||||
if (aValue.isBoolean() && !aValue.toBoolean()) {
|
||||
// Things went fine, not handled by app, let Gecko do its thing
|
||||
mLoadData.Run();
|
||||
} else if (!aValue.isBoolean()) {
|
||||
// If the promise resolves to a non-boolean, let Gecko handle the load
|
||||
mLoadData.Run();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
||||
{
|
||||
// In the event of a rejected callback, let Gecko handle the load
|
||||
mLoadData.Run();
|
||||
}
|
||||
|
||||
private:
|
||||
~LoadURIDelegateHandler()
|
||||
{}
|
||||
|
||||
InternalLoadData mLoadData;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(LoadURIDelegateHandler, mLoadData.mDocShell,
|
||||
mLoadData.mURI, mLoadData.mOriginalURI,
|
||||
mLoadData.mResultPrincipalURI, mLoadData.mReferrer,
|
||||
mLoadData.mTriggeringPrincipal,
|
||||
mLoadData.mPrincipalToInherit,
|
||||
mLoadData.mPostData, mLoadData.mHeadersData,
|
||||
mLoadData.mSHEntry, mLoadData.mSourceDocShell,
|
||||
mLoadData.mBaseURI)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadURIDelegateHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadURIDelegateHandler)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadURIDelegateHandler)
|
||||
|
||||
/**
|
||||
* Returns true if we started an asynchronous load (i.e., from the network), but
|
||||
* the document we're loading there hasn't yet become this docshell's active
|
||||
|
@ -9563,10 +9396,7 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
const bool isDocumentAuxSandboxed = doc &&
|
||||
(doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
|
||||
|
||||
const bool checkLoadDelegates = !(aFlags & INTERNAL_LOAD_FLAGS_DELEGATES_CHECKED);
|
||||
aFlags = aFlags & ~INTERNAL_LOAD_FLAGS_DELEGATES_CHECKED;
|
||||
|
||||
if (aURI && mLoadURIDelegate && checkLoadDelegates && aLoadType != LOAD_ERROR_PAGE &&
|
||||
if (aURI && mLoadURIDelegate && aLoadType != LOAD_ERROR_PAGE &&
|
||||
(!targetDocShell || targetDocShell == static_cast<nsIDocShell*>(this))) {
|
||||
// Dispatch only load requests for the current or a new window to the
|
||||
// delegate, e.g., to allow for GeckoView apps to handle the load event
|
||||
|
@ -9579,24 +9409,11 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
|
||||
}
|
||||
|
||||
RefPtr<dom::Promise> promise;
|
||||
bool loadURIHandled = false;
|
||||
rv = mLoadURIDelegate->LoadURI(aURI, where, aFlags, aTriggeringPrincipal,
|
||||
getter_AddRefs(promise));
|
||||
if (NS_SUCCEEDED(rv) && promise) {
|
||||
const uint32_t flags = aFlags | INTERNAL_LOAD_FLAGS_DELEGATES_CHECKED;
|
||||
|
||||
RefPtr<LoadURIDelegateHandler> handler =
|
||||
new LoadURIDelegateHandler(this, aURI, aOriginalURI, aResultPrincipalURI,
|
||||
aKeepResultPrincipalURIIfSet,
|
||||
aLoadReplace, aReferrer, aReferrerPolicy,
|
||||
aTriggeringPrincipal, aPrincipalToInherit,
|
||||
flags, aWindowTarget, aTypeHint, aFileName, aPostData,
|
||||
aHeadersData, aLoadType, aSHEntry, aFirstParty,
|
||||
aSrcdoc, aSourceDocShell, aBaseURI, nullptr, nullptr);
|
||||
|
||||
promise->AppendNativeHandler(handler);
|
||||
|
||||
// Checking for load delegates; InternalLoad will be re-called if needed.
|
||||
&loadURIHandled);
|
||||
if (NS_SUCCEEDED(rv) && loadURIHandled) {
|
||||
// The request has been handled, nothing to do here.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,9 +119,6 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
// Whether a top-level data URI navigation is allowed for that load
|
||||
const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI = 0x200;
|
||||
|
||||
// Whether load delegates have already been checked for this load
|
||||
const long INTERNAL_LOAD_FLAGS_DELEGATES_CHECKED = 0x400;
|
||||
|
||||
// Whether the load was triggered by user interaction.
|
||||
const long INTERNAL_LOAD_FLAGS_IS_USER_TRIGGERED = 0x1000;
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ nsSHEntry::nsSHEntry(const nsSHEntry& aOther)
|
|||
, mIsSrcdocEntry(aOther.mIsSrcdocEntry)
|
||||
, mScrollRestorationIsManual(false)
|
||||
, mLoadedInThisProcess(aOther.mLoadedInThisProcess)
|
||||
, mPersist(aOther.mPersist)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/.
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Device Interfaces")
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsISettingsService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_settings'
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(aad47850-2e87-11e2-81c1-0800200c9a66)]
|
||||
interface nsISettingsServiceCallback : nsISupports
|
||||
{
|
||||
void handle(in DOMString aName, in jsval aResult);
|
||||
void handleError(in DOMString aErrorMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f1b3d820-8e75-11e3-baa8-0800200c9a66)]
|
||||
interface nsISettingsTransactionCompleteCallback : nsISupports
|
||||
{
|
||||
void handle();
|
||||
void handleAbort(in DOMString aErrorMessage);
|
||||
};
|
||||
|
||||
[scriptable, uuid(d7a395a0-e292-11e1-834e-1761d57f5f99)]
|
||||
interface nsISettingsServiceLock : nsISupports
|
||||
{
|
||||
void set(in string aName,
|
||||
in jsval aValue,
|
||||
in nsISettingsServiceCallback aCallback,
|
||||
[optional] in string aMessage);
|
||||
|
||||
void get(in string aName, in nsISettingsServiceCallback aCallback);
|
||||
};
|
||||
|
||||
[scriptable, uuid(d1ed155c-9f90-47bb-91c2-7eac54d69f4b)]
|
||||
interface nsISettingsService : nsISupports
|
||||
{
|
||||
nsISettingsServiceLock createLock([optional] in nsISettingsTransactionCompleteCallback aCallback);
|
||||
void receiveMessage(in jsval aMessage);
|
||||
};
|
|
@ -24,7 +24,7 @@ use prim_store::{BorderSource, Primitive, PrimitiveDetails};
|
|||
use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree};
|
||||
use renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
|
||||
use renderer::BLOCKS_PER_UV_RECT;
|
||||
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
|
||||
use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
|
||||
use scene::FilterOpHelpers;
|
||||
use std::{f32, i32};
|
||||
use tiling::{RenderTargetContext};
|
||||
|
@ -1604,6 +1604,30 @@ impl Primitive {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_cacheable(
|
||||
&self,
|
||||
resource_cache: &ResourceCache
|
||||
) -> bool {
|
||||
let image_key = match self.details {
|
||||
PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::Image{ request, .. }, .. }) => {
|
||||
request.key
|
||||
}
|
||||
PrimitiveDetails::Brush(BrushPrimitive { kind: BrushKind::YuvImage{ yuv_key, .. }, .. }) => {
|
||||
yuv_key[0]
|
||||
}
|
||||
PrimitiveDetails::Brush(_) |
|
||||
PrimitiveDetails::TextRun(..) => {
|
||||
return true
|
||||
}
|
||||
};
|
||||
match resource_cache.get_image_properties(image_key) {
|
||||
Some(ImageProperties { external_image: Some(_), .. }) => {
|
||||
false
|
||||
}
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PictureSurface {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use api::{BorderRadius, BorderSide, BorderStyle, BorderWidths, ColorF};
|
||||
use api::{ColorU, DeviceRect, DeviceSize, LayoutSizeAu, LayoutPrimitiveInfo, LayoutToDeviceScale};
|
||||
use api::{DevicePixel, DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
|
||||
use api::{DeviceVector2D, DevicePoint, DeviceIntSize, LayoutRect, LayoutSize, NormalBorder};
|
||||
use app_units::Au;
|
||||
use ellipse::Ellipse;
|
||||
use display_list_flattener::DisplayListFlattener;
|
||||
|
@ -24,7 +24,7 @@ pub const MAX_BORDER_RESOLUTION: u32 = 2048;
|
|||
/// Maximum number of dots or dashes per segment to avoid freezing and filling up
|
||||
/// memory with unreasonable inputs. It would be better to address this by not building
|
||||
/// a list of per-dot information in the first place.
|
||||
pub const MAX_DASH_COUNT: usize = 2048;
|
||||
pub const MAX_DASH_COUNT: u32 = 2048;
|
||||
|
||||
trait AuSizeConverter {
|
||||
fn to_au(&self) -> LayoutSizeAu;
|
||||
|
@ -241,283 +241,271 @@ pub enum BorderClipKind {
|
|||
Dot = 3,
|
||||
}
|
||||
|
||||
/// The source data for a border corner clip mask.
|
||||
#[derive(Debug, Clone)]
|
||||
struct BorderCornerClipSource {
|
||||
// FIXME(emilio): the `max_clip_count` name makes no sense for dashed
|
||||
// borders now that it represents half-dashes.
|
||||
max_clip_count: usize,
|
||||
kind: BorderClipKind,
|
||||
widths: DeviceSize,
|
||||
fn compute_outer_and_clip_sign(
|
||||
corner_segment: BorderSegment,
|
||||
radius: DeviceSize,
|
||||
ellipse: Ellipse<DevicePixel>,
|
||||
) -> (DevicePoint, DeviceVector2D) {
|
||||
let outer_scale = match corner_segment {
|
||||
BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
|
||||
BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
|
||||
BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0),
|
||||
BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0),
|
||||
_ => panic!("bug: expected a corner segment"),
|
||||
};
|
||||
let outer = DevicePoint::new(
|
||||
outer_scale.x * radius.width,
|
||||
outer_scale.y * radius.height,
|
||||
);
|
||||
|
||||
let clip_sign = DeviceVector2D::new(
|
||||
1.0 - 2.0 * outer_scale.x,
|
||||
1.0 - 2.0 * outer_scale.y,
|
||||
);
|
||||
|
||||
(outer, clip_sign)
|
||||
}
|
||||
|
||||
impl BorderCornerClipSource {
|
||||
pub fn new(
|
||||
corner_radius: DeviceSize,
|
||||
widths: DeviceSize,
|
||||
kind: BorderClipKind,
|
||||
) -> BorderCornerClipSource {
|
||||
// Work out a dash length (and therefore dash count)
|
||||
// based on the width of the border edges. The "correct"
|
||||
// dash length is not mentioned in the CSS borders
|
||||
// spec. The calculation below is similar, but not exactly
|
||||
// the same as what Gecko uses.
|
||||
// TODO(gw): Iterate on this to get it closer to what Gecko
|
||||
// uses for dash length.
|
||||
fn write_dashed_corner_instances(
|
||||
corner_radius: DeviceSize,
|
||||
widths: DeviceSize,
|
||||
segment: BorderSegment,
|
||||
base_instance: &BorderInstance,
|
||||
instances: &mut Vec<BorderInstance>,
|
||||
) -> Result<(), ()> {
|
||||
let ellipse = Ellipse::new(corner_radius);
|
||||
|
||||
let (ellipse, max_clip_count) = match kind {
|
||||
BorderClipKind::DashEdge => unreachable!("not for corners"),
|
||||
BorderClipKind::DashCorner => {
|
||||
let ellipse = Ellipse::new(corner_radius);
|
||||
let average_border_width = 0.5 * (widths.width + widths.height);
|
||||
|
||||
let average_border_width = 0.5 * (widths.width + widths.height);
|
||||
let (_half_dash, num_half_dashes) =
|
||||
compute_half_dash(average_border_width, ellipse.total_arc_length);
|
||||
|
||||
let (_half_dash, num_half_dashes) =
|
||||
compute_half_dash(average_border_width, ellipse.total_arc_length);
|
||||
if num_half_dashes == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Round that up to the nearest integer, so that the dash length
|
||||
// doesn't exceed the ratio above. Add one extra dash to cover
|
||||
// the last half-dash of the arc.
|
||||
(ellipse, num_half_dashes as usize)
|
||||
}
|
||||
BorderClipKind::Dot => {
|
||||
let mut corner_radius = corner_radius;
|
||||
if corner_radius.width < (widths.width / 2.0) {
|
||||
corner_radius.width = 0.0;
|
||||
}
|
||||
if corner_radius.height < (widths.height / 2.0) {
|
||||
corner_radius.height = 0.0;
|
||||
}
|
||||
let num_half_dashes = num_half_dashes.min(MAX_DASH_COUNT);
|
||||
|
||||
if corner_radius.width == 0. && corner_radius.height == 0. {
|
||||
(Ellipse::new(corner_radius), 1)
|
||||
} else {
|
||||
// The centers of dots follow an ellipse along the middle of the
|
||||
// border radius.
|
||||
let inner_radius = (corner_radius - widths * 0.5).abs();
|
||||
let ellipse = Ellipse::new(inner_radius);
|
||||
let (outer, clip_sign) = compute_outer_and_clip_sign(segment, corner_radius);
|
||||
|
||||
// Allocate a "worst case" number of dot clips. This can be
|
||||
// calculated by taking the minimum edge radius, since that
|
||||
// will result in the maximum number of dots along the path.
|
||||
let min_diameter = widths.width.min(widths.height);
|
||||
let instance_count = num_half_dashes / 4 + 1;
|
||||
instances.reserve(instance_count as usize);
|
||||
|
||||
// Get the number of circles (assuming spacing of one diameter
|
||||
// between dots).
|
||||
let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
|
||||
let half_dash_arc_length =
|
||||
ellipse.total_arc_length / num_half_dashes as f32;
|
||||
let dash_length = 2. * half_dash_arc_length;
|
||||
|
||||
// Add space for one extra dot since they are centered at the
|
||||
// start of the arc.
|
||||
(ellipse, max_dot_count.ceil() as usize)
|
||||
}
|
||||
}
|
||||
let mut current_length = 0.;
|
||||
for i in 0..instance_count {
|
||||
let arc_length0 = current_length;
|
||||
current_length += if i == 0 {
|
||||
half_dash_arc_length
|
||||
} else {
|
||||
dash_length
|
||||
};
|
||||
|
||||
BorderCornerClipSource {
|
||||
kind,
|
||||
max_clip_count,
|
||||
ellipse,
|
||||
widths,
|
||||
radius: corner_radius,
|
||||
let arc_length1 = current_length;
|
||||
current_length += dash_length;
|
||||
|
||||
let alpha = ellipse.find_angle_for_arc_length(arc_length0);
|
||||
let beta = ellipse.find_angle_for_arc_length(arc_length1);
|
||||
|
||||
let (point0, tangent0) = ellipse.get_point_and_tangent(alpha);
|
||||
let (point1, tangent1) = ellipse.get_point_and_tangent(beta);
|
||||
|
||||
let point0 = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (corner_radius.width - point0.x),
|
||||
outer.y + clip_sign.y * (corner_radius.height - point0.y),
|
||||
);
|
||||
|
||||
let tangent0 = DeviceVector2D::new(
|
||||
-tangent0.x * clip_sign.x,
|
||||
-tangent0.y * clip_sign.y,
|
||||
);
|
||||
|
||||
let point1 = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (corner_radius.width - point1.x),
|
||||
outer.y + clip_sign.y * (corner_radius.height - point1.y),
|
||||
);
|
||||
|
||||
let tangent1 = DeviceVector2D::new(
|
||||
-tangent1.x * clip_sign.x,
|
||||
-tangent1.y * clip_sign.y,
|
||||
);
|
||||
|
||||
instances.push(BorderInstance {
|
||||
flags: base_instance.flags | ((BorderClipKind::DashCorner as i32) << 24),
|
||||
clip_params: [
|
||||
point0.x,
|
||||
point0.y,
|
||||
tangent0.x,
|
||||
tangent0.y,
|
||||
point1.x,
|
||||
point1.y,
|
||||
tangent1.x,
|
||||
tangent1.y,
|
||||
],
|
||||
.. *base_instance
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_dotted_corner_instances(
|
||||
corner_radius: DeviceSize,
|
||||
widths: DeviceSize,
|
||||
segment: BorderSegment,
|
||||
base_instance: &BorderInstance,
|
||||
instances: &mut Vec<BorderInstance>,
|
||||
) -> Result<(), ()> {
|
||||
let mut corner_radius = corner_radius;
|
||||
if corner_radius.width < (widths.width / 2.0) {
|
||||
corner_radius.width = 0.0;
|
||||
}
|
||||
if corner_radius.height < (widths.height / 2.0) {
|
||||
corner_radius.height = 0.0;
|
||||
}
|
||||
|
||||
let (ellipse, max_dot_count) =
|
||||
if corner_radius.width == 0. && corner_radius.height == 0. {
|
||||
(Ellipse::new(corner_radius), 1)
|
||||
} else {
|
||||
// The centers of dots follow an ellipse along the middle of the
|
||||
// border radius.
|
||||
let inner_radius = (corner_radius - widths * 0.5).abs();
|
||||
let ellipse = Ellipse::new(inner_radius);
|
||||
|
||||
// Allocate a "worst case" number of dot clips. This can be
|
||||
// calculated by taking the minimum edge radius, since that
|
||||
// will result in the maximum number of dots along the path.
|
||||
let min_diameter = widths.width.min(widths.height);
|
||||
|
||||
// Get the number of circles (assuming spacing of one diameter
|
||||
// between dots).
|
||||
let max_dot_count = 0.5 * ellipse.total_arc_length / min_diameter;
|
||||
|
||||
// Add space for one extra dot since they are centered at the
|
||||
// start of the arc.
|
||||
(ellipse, max_dot_count.ceil() as usize)
|
||||
};
|
||||
|
||||
if max_dot_count == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if max_dot_count == 1 {
|
||||
let dot_diameter = lerp(widths.width, widths.height, 0.5);
|
||||
instances.push(BorderInstance {
|
||||
flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
|
||||
clip_params: [
|
||||
widths.width / 2.0, widths.height / 2.0, 0.5 * dot_diameter, 0.,
|
||||
0., 0., 0., 0.,
|
||||
],
|
||||
.. *base_instance
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let max_dot_count = max_dot_count.min(MAX_DASH_COUNT as usize);
|
||||
|
||||
// FIXME(emilio): Should probably use SmallVec.
|
||||
let mut forward_dots = Vec::with_capacity(max_dot_count / 2 + 1);
|
||||
let mut back_dots = Vec::with_capacity(max_dot_count / 2 + 1);
|
||||
let mut leftover_arc_length = 0.0;
|
||||
|
||||
// Alternate between adding dots at the start and end of the
|
||||
// ellipse arc. This ensures that we always end up with an exact
|
||||
// half dot at each end of the arc, to match up with the edges.
|
||||
forward_dots.push(DotInfo::new(widths.width, widths.width));
|
||||
back_dots.push(DotInfo::new(
|
||||
ellipse.total_arc_length - widths.height,
|
||||
widths.height,
|
||||
));
|
||||
|
||||
let (outer, clip_sign) = compute_outer_and_clip_sign(segment, corner_radius);
|
||||
for dot_index in 0 .. max_dot_count {
|
||||
let prev_forward_pos = *forward_dots.last().unwrap();
|
||||
let prev_back_pos = *back_dots.last().unwrap();
|
||||
|
||||
// Select which end of the arc to place a dot from.
|
||||
// This just alternates between the start and end of
|
||||
// the arc, which ensures that there is always an
|
||||
// exact half-dot at each end of the ellipse.
|
||||
let going_forward = dot_index & 1 == 0;
|
||||
|
||||
let (next_dot_pos, leftover) = if going_forward {
|
||||
let next_dot_pos =
|
||||
prev_forward_pos.arc_pos + 2.0 * prev_forward_pos.diameter;
|
||||
(next_dot_pos, prev_back_pos.arc_pos - next_dot_pos)
|
||||
} else {
|
||||
let next_dot_pos = prev_back_pos.arc_pos - 2.0 * prev_back_pos.diameter;
|
||||
(next_dot_pos, next_dot_pos - prev_forward_pos.arc_pos)
|
||||
};
|
||||
|
||||
// Use a lerp between each edge's dot
|
||||
// diameter, based on the linear distance
|
||||
// along the arc to get the diameter of the
|
||||
// dot at this arc position.
|
||||
let t = next_dot_pos / ellipse.total_arc_length;
|
||||
let dot_diameter = lerp(widths.width, widths.height, t);
|
||||
|
||||
// If we can't fit a dot, bail out.
|
||||
if leftover < dot_diameter {
|
||||
leftover_arc_length = leftover;
|
||||
break;
|
||||
}
|
||||
|
||||
// We can place a dot!
|
||||
let dot = DotInfo::new(next_dot_pos, dot_diameter);
|
||||
if going_forward {
|
||||
forward_dots.push(dot);
|
||||
} else {
|
||||
back_dots.push(dot);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gw): The naming and structure of BorderCornerClipSource
|
||||
// don't really make sense. I've left it this way
|
||||
// for now in order to reduce the size of the
|
||||
// patch a bit. In the future, when we spent some
|
||||
// time working on dot/dash placement, we should
|
||||
// restructure this code to be more consistent
|
||||
// with how border rendering works now.
|
||||
pub fn write(self, segment: BorderSegment) -> Vec<[f32; 8]> {
|
||||
let mut dot_dash_data = Vec::new();
|
||||
// Now step through the dots, and distribute any extra
|
||||
// leftover space on the arc between them evenly. Once
|
||||
// the final arc position is determined, generate the correct
|
||||
// arc positions and angles that get passed to the clip shader.
|
||||
let number_of_dots = forward_dots.len() + back_dots.len();
|
||||
let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32;
|
||||
|
||||
if self.max_clip_count == 0 {
|
||||
return dot_dash_data;
|
||||
}
|
||||
let create_dot_data = |arc_length: f32, dot_radius: f32| -> [f32; 8] {
|
||||
// Represents the GPU data for drawing a single dot to a clip mask. The order
|
||||
// these are specified must stay in sync with the way this data is read in the
|
||||
// dot clip shader.
|
||||
let theta = ellipse.find_angle_for_arc_length(arc_length);
|
||||
let (center, _) = ellipse.get_point_and_tangent(theta);
|
||||
|
||||
let outer_scale = match segment {
|
||||
BorderSegment::TopLeft => DeviceVector2D::new(0.0, 0.0),
|
||||
BorderSegment::TopRight => DeviceVector2D::new(1.0, 0.0),
|
||||
BorderSegment::BottomRight => DeviceVector2D::new(1.0, 1.0),
|
||||
BorderSegment::BottomLeft => DeviceVector2D::new(0.0, 1.0),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let outer = DevicePoint::new(
|
||||
outer_scale.x * self.radius.width,
|
||||
outer_scale.y * self.radius.height,
|
||||
);
|
||||
let clip_sign = DeviceVector2D::new(
|
||||
1.0 - 2.0 * outer_scale.x,
|
||||
1.0 - 2.0 * outer_scale.y,
|
||||
let center = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (corner_radius.width - center.x),
|
||||
outer.y + clip_sign.y * (corner_radius.height - center.y),
|
||||
);
|
||||
|
||||
let max_clip_count = self.max_clip_count.min(MAX_DASH_COUNT);
|
||||
[center.x, center.y, dot_radius, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
};
|
||||
|
||||
match self.kind {
|
||||
BorderClipKind::DashEdge => unreachable!("not for corners"),
|
||||
BorderClipKind::DashCorner => {
|
||||
// Get the correct half-dash arc length.
|
||||
let half_dash_arc_length =
|
||||
self.ellipse.total_arc_length / max_clip_count as f32;
|
||||
let dash_length = 2. * half_dash_arc_length;
|
||||
|
||||
let mut current_length = 0.;
|
||||
|
||||
dot_dash_data.reserve(max_clip_count / 4 + 1);
|
||||
for i in 0 .. (max_clip_count / 4 + 1) {
|
||||
let arc_length0 = current_length;
|
||||
current_length += if i == 0 {
|
||||
half_dash_arc_length
|
||||
} else {
|
||||
dash_length
|
||||
};
|
||||
|
||||
let arc_length1 = current_length;
|
||||
current_length += dash_length;
|
||||
|
||||
let alpha = self.ellipse.find_angle_for_arc_length(arc_length0);
|
||||
let beta = self.ellipse.find_angle_for_arc_length(arc_length1);
|
||||
|
||||
let (point0, tangent0) = self.ellipse.get_point_and_tangent(alpha);
|
||||
let (point1, tangent1) = self.ellipse.get_point_and_tangent(beta);
|
||||
|
||||
let point0 = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (self.radius.width - point0.x),
|
||||
outer.y + clip_sign.y * (self.radius.height - point0.y),
|
||||
);
|
||||
|
||||
let tangent0 = DeviceVector2D::new(
|
||||
-tangent0.x * clip_sign.x,
|
||||
-tangent0.y * clip_sign.y,
|
||||
);
|
||||
|
||||
let point1 = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (self.radius.width - point1.x),
|
||||
outer.y + clip_sign.y * (self.radius.height - point1.y),
|
||||
);
|
||||
|
||||
let tangent1 = DeviceVector2D::new(
|
||||
-tangent1.x * clip_sign.x,
|
||||
-tangent1.y * clip_sign.y,
|
||||
);
|
||||
|
||||
dot_dash_data.push([
|
||||
point0.x,
|
||||
point0.y,
|
||||
tangent0.x,
|
||||
tangent0.y,
|
||||
point1.x,
|
||||
point1.y,
|
||||
tangent1.x,
|
||||
tangent1.y,
|
||||
]);
|
||||
}
|
||||
}
|
||||
BorderClipKind::Dot if max_clip_count == 1 => {
|
||||
let dot_diameter = lerp(self.widths.width, self.widths.height, 0.5);
|
||||
dot_dash_data.push([
|
||||
self.widths.width / 2.0, self.widths.height / 2.0, 0.5 * dot_diameter, 0.,
|
||||
0., 0., 0., 0.,
|
||||
]);
|
||||
}
|
||||
BorderClipKind::Dot => {
|
||||
let mut forward_dots = Vec::with_capacity(max_clip_count / 2 + 1);
|
||||
let mut back_dots = Vec::with_capacity(max_clip_count / 2 + 1);
|
||||
let mut leftover_arc_length = 0.0;
|
||||
|
||||
// Alternate between adding dots at the start and end of the
|
||||
// ellipse arc. This ensures that we always end up with an exact
|
||||
// half dot at each end of the arc, to match up with the edges.
|
||||
forward_dots.push(DotInfo::new(self.widths.width, self.widths.width));
|
||||
back_dots.push(DotInfo::new(
|
||||
self.ellipse.total_arc_length - self.widths.height,
|
||||
self.widths.height,
|
||||
));
|
||||
|
||||
for dot_index in 0 .. max_clip_count {
|
||||
let prev_forward_pos = *forward_dots.last().unwrap();
|
||||
let prev_back_pos = *back_dots.last().unwrap();
|
||||
|
||||
// Select which end of the arc to place a dot from.
|
||||
// This just alternates between the start and end of
|
||||
// the arc, which ensures that there is always an
|
||||
// exact half-dot at each end of the ellipse.
|
||||
let going_forward = dot_index & 1 == 0;
|
||||
|
||||
let (next_dot_pos, leftover) = if going_forward {
|
||||
let next_dot_pos =
|
||||
prev_forward_pos.arc_pos + 2.0 * prev_forward_pos.diameter;
|
||||
(next_dot_pos, prev_back_pos.arc_pos - next_dot_pos)
|
||||
} else {
|
||||
let next_dot_pos = prev_back_pos.arc_pos - 2.0 * prev_back_pos.diameter;
|
||||
(next_dot_pos, next_dot_pos - prev_forward_pos.arc_pos)
|
||||
};
|
||||
|
||||
// Use a lerp between each edge's dot
|
||||
// diameter, based on the linear distance
|
||||
// along the arc to get the diameter of the
|
||||
// dot at this arc position.
|
||||
let t = next_dot_pos / self.ellipse.total_arc_length;
|
||||
let dot_diameter = lerp(self.widths.width, self.widths.height, t);
|
||||
|
||||
// If we can't fit a dot, bail out.
|
||||
if leftover < dot_diameter {
|
||||
leftover_arc_length = leftover;
|
||||
break;
|
||||
}
|
||||
|
||||
// We can place a dot!
|
||||
let dot = DotInfo::new(next_dot_pos, dot_diameter);
|
||||
if going_forward {
|
||||
forward_dots.push(dot);
|
||||
} else {
|
||||
back_dots.push(dot);
|
||||
}
|
||||
}
|
||||
|
||||
// Now step through the dots, and distribute any extra
|
||||
// leftover space on the arc between them evenly. Once
|
||||
// the final arc position is determined, generate the correct
|
||||
// arc positions and angles that get passed to the clip shader.
|
||||
let number_of_dots = forward_dots.len() + back_dots.len();
|
||||
let extra_space_per_dot = leftover_arc_length / (number_of_dots - 1) as f32;
|
||||
|
||||
let create_dot_data = |ellipse: &Ellipse<DevicePixel>, arc_length: f32, radius: f32| -> [f32; 8] {
|
||||
// Represents the GPU data for drawing a single dot to a clip mask. The order
|
||||
// these are specified must stay in sync with the way this data is read in the
|
||||
// dot clip shader.
|
||||
let theta = ellipse.find_angle_for_arc_length(arc_length);
|
||||
let (center, _) = ellipse.get_point_and_tangent(theta);
|
||||
|
||||
let center = DevicePoint::new(
|
||||
outer.x + clip_sign.x * (self.radius.width - center.x),
|
||||
outer.y + clip_sign.y * (self.radius.height - center.y),
|
||||
);
|
||||
|
||||
[center.x, center.y, radius, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
};
|
||||
|
||||
dot_dash_data.reserve(forward_dots.len() + back_dots.len());
|
||||
|
||||
for (i, dot) in forward_dots.iter().enumerate() {
|
||||
let extra_dist = i as f32 * extra_space_per_dot;
|
||||
let dot_data = create_dot_data(&self.ellipse, dot.arc_pos + extra_dist, 0.5 * dot.diameter);
|
||||
dot_dash_data.push(dot_data);
|
||||
}
|
||||
|
||||
for (i, dot) in back_dots.iter().enumerate() {
|
||||
let extra_dist = i as f32 * extra_space_per_dot;
|
||||
let dot_data = create_dot_data(&self.ellipse, dot.arc_pos - extra_dist, 0.5 * dot.diameter);
|
||||
dot_dash_data.push(dot_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dot_dash_data
|
||||
instances.reserve(number_of_dots);
|
||||
for (i, dot) in forward_dots.iter().enumerate() {
|
||||
let extra_dist = i as f32 * extra_space_per_dot;
|
||||
instances.push(BorderInstance {
|
||||
flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
|
||||
clip_params: create_dot_data(dot.arc_pos + extra_dist, 0.5 * dot.diameter),
|
||||
.. *base_instance
|
||||
});
|
||||
}
|
||||
|
||||
for (i, dot) in back_dots.iter().enumerate() {
|
||||
let extra_dist = i as f32 * extra_space_per_dot;
|
||||
instances.push(BorderInstance {
|
||||
flags: base_instance.flags | ((BorderClipKind::Dot as i32) << 24),
|
||||
clip_params: create_dot_data(dot.arc_pos - extra_dist, 0.5 * dot.diameter),
|
||||
.. *base_instance
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -1042,39 +1030,30 @@ fn add_segment(
|
|||
warn!("TODO: Handle a corner with dotted / dashed transition.");
|
||||
}
|
||||
|
||||
let clip_kind = match style0 {
|
||||
BorderStyle::Dashed => Some(BorderClipKind::DashCorner),
|
||||
BorderStyle::Dotted => Some(BorderClipKind::Dot),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match clip_kind {
|
||||
Some(clip_kind) => {
|
||||
let clip_source = BorderCornerClipSource::new(
|
||||
let dashed_or_dotted_corner = match style0 {
|
||||
BorderStyle::Dashed => {
|
||||
write_dashed_corner_instances(
|
||||
radius,
|
||||
widths,
|
||||
clip_kind,
|
||||
);
|
||||
|
||||
// TODO(gw): Restructure the BorderCornerClipSource code
|
||||
// so that we don't allocate a Vec here.
|
||||
let clip_list = clip_source.write(segment);
|
||||
|
||||
if clip_list.is_empty() {
|
||||
instances.push(base_instance);
|
||||
} else {
|
||||
for params in clip_list {
|
||||
instances.push(BorderInstance {
|
||||
flags: base_flags | ((clip_kind as i32) << 24),
|
||||
clip_params: params,
|
||||
..base_instance
|
||||
});
|
||||
}
|
||||
}
|
||||
segment,
|
||||
&base_instance,
|
||||
instances,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
instances.push(base_instance);
|
||||
BorderStyle::Dotted => {
|
||||
write_dotted_corner_instances(
|
||||
radius,
|
||||
widths,
|
||||
segment,
|
||||
&base_instance,
|
||||
instances,
|
||||
)
|
||||
}
|
||||
_ => Err(()),
|
||||
};
|
||||
|
||||
if dashed_or_dotted_corner.is_err() {
|
||||
instances.push(base_instance);
|
||||
}
|
||||
}
|
||||
BorderSegment::Top |
|
||||
|
|
|
@ -100,6 +100,7 @@ pub struct PictureContext {
|
|||
pub struct PictureState {
|
||||
pub tasks: Vec<RenderTaskId>,
|
||||
pub has_non_root_coord_system: bool,
|
||||
pub is_cacheable: bool,
|
||||
pub local_rect_changed: bool,
|
||||
pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
|
||||
pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
|
||||
|
|
|
@ -291,6 +291,7 @@ impl PicturePrimitive {
|
|||
let state = PictureState {
|
||||
tasks: Vec::new(),
|
||||
has_non_root_coord_system: false,
|
||||
is_cacheable: true,
|
||||
local_rect_changed: false,
|
||||
raster_spatial_node_index,
|
||||
surface_spatial_node_index,
|
||||
|
@ -472,7 +473,8 @@ impl PicturePrimitive {
|
|||
// anyway. In the future we should relax this a bit, so that we can
|
||||
// cache tasks with complex coordinate systems if we detect the
|
||||
// relevant transforms haven't changed from frame to frame.
|
||||
let surface = if pic_state_for_children.has_non_root_coord_system {
|
||||
let surface = if pic_state_for_children.has_non_root_coord_system ||
|
||||
!pic_state_for_children.is_cacheable {
|
||||
let picture_task = RenderTask::new_picture(
|
||||
RenderTaskLocation::Dynamic(None, device_rect.size),
|
||||
unclipped.size,
|
||||
|
|
|
@ -1651,6 +1651,10 @@ impl PrimitiveStore {
|
|||
Some(pic_rect)
|
||||
};
|
||||
|
||||
if !pic_state_for_children.is_cacheable {
|
||||
pic_state.is_cacheable = false;
|
||||
}
|
||||
|
||||
// Restore the dependencies (borrow check dance)
|
||||
let prim = &mut self.primitives[prim_index.0];
|
||||
let (new_local_rect, clip_node_collector) = prim
|
||||
|
@ -1677,6 +1681,10 @@ impl PrimitiveStore {
|
|||
|
||||
let prim = &mut self.primitives[prim_index.0];
|
||||
|
||||
if !prim.is_cacheable(frame_state.resource_cache) {
|
||||
pic_state.is_cacheable = false;
|
||||
}
|
||||
|
||||
if is_passthrough {
|
||||
prim.metadata.clipped_world_rect = Some(pic_state.map_pic_to_world.bounds);
|
||||
} else {
|
||||
|
|
|
@ -1 +1 @@
|
|||
70edb5f8a75ea1e1440ba7984cc42df9eb05ae69
|
||||
0f142521b86f201a0f0957cc852aa14923ebfc73
|
||||
|
|
|
@ -365,11 +365,12 @@ ClippedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
ClippedImage::GetImageContainerAtSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
ClippedImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
// XXX(seth): We currently don't have a way of clipping the result of
|
||||
// GetImageContainer. We work around this by always returning null, but if it
|
||||
|
@ -378,11 +379,11 @@ ClippedImage::GetImageContainerAtSize(LayerManager* aManager,
|
|||
// that method for performance reasons.
|
||||
|
||||
if (!ShouldClip()) {
|
||||
return InnerImage()->GetImageContainerAtSize(aManager, aSize,
|
||||
aSVGContext, aFlags);
|
||||
return InnerImage()->GetImageContainerAtSize(aManager, aSize, aSVGContext,
|
||||
aFlags, aOutContainer);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
|
@ -51,11 +51,12 @@ public:
|
|||
IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
|
||||
NS_IMETHOD_(ImgDrawResult)
|
||||
GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags) override;
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer) override;
|
||||
NS_IMETHOD_(ImgDrawResult) Draw(gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
const ImageRegion& aRegion,
|
||||
|
|
|
@ -234,13 +234,14 @@ DynamicImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
DynamicImage::GetImageContainerAtSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
DynamicImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aContainer)
|
||||
{
|
||||
return nullptr;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
|
|
|
@ -77,18 +77,19 @@ FrozenImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
FrozenImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
// XXX(seth): GetImageContainer does not currently support anything but the
|
||||
// current frame. We work around this by always returning null, but if it ever
|
||||
// turns out that FrozenImage is widely used on codepaths that can actually
|
||||
// benefit from GetImageContainer, it would be a good idea to fix that method
|
||||
// for performance reasons.
|
||||
return nullptr;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
|
|
|
@ -50,11 +50,12 @@ public:
|
|||
IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
|
||||
NS_IMETHOD_(ImgDrawResult)
|
||||
GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags) override;
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer) override;
|
||||
NS_IMETHOD_(ImgDrawResult) Draw(gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
const ImageRegion& aRegion,
|
||||
|
|
|
@ -104,11 +104,12 @@ ImageResource::SetCurrentImage(ImageContainer* aContainer,
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<ImageContainer>
|
||||
ImgDrawResult
|
||||
ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
ImageContainer** aOutContainer)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aManager);
|
||||
|
@ -119,11 +120,15 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
== FLAG_NONE,
|
||||
"Unsupported flag passed to GetImageContainer");
|
||||
|
||||
IntSize size = GetImageContainerSize(aManager, aSize, aFlags);
|
||||
if (size.IsEmpty()) {
|
||||
return nullptr;
|
||||
ImgDrawResult drawResult;
|
||||
IntSize size;
|
||||
Tie(drawResult, size) = GetImageContainerSize(aManager, aSize, aFlags);
|
||||
if (drawResult != ImgDrawResult::SUCCESS) {
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!size.IsEmpty());
|
||||
|
||||
if (mAnimationConsumers == 0) {
|
||||
SendOnUnlockedDraw(aFlags);
|
||||
}
|
||||
|
@ -155,7 +160,8 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
case ImgDrawResult::SUCCESS:
|
||||
case ImgDrawResult::BAD_IMAGE:
|
||||
case ImgDrawResult::BAD_ARGS:
|
||||
return container.forget();
|
||||
container.forget(aOutContainer);
|
||||
return entry->mLastDrawResult;
|
||||
case ImgDrawResult::NOT_READY:
|
||||
case ImgDrawResult::INCOMPLETE:
|
||||
case ImgDrawResult::TEMPORARY_ERROR:
|
||||
|
@ -165,7 +171,8 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
// Unused by GetFrameInternal
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unhandled ImgDrawResult type!");
|
||||
return container.forget();
|
||||
container.forget(aOutContainer);
|
||||
return entry->mLastDrawResult;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +180,6 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
NotifyDrawingObservers();
|
||||
#endif
|
||||
|
||||
ImgDrawResult drawResult;
|
||||
IntSize bestSize;
|
||||
RefPtr<SourceSurface> surface;
|
||||
Tie(drawResult, bestSize, surface) =
|
||||
|
@ -212,7 +218,8 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
case ImgDrawResult::SUCCESS:
|
||||
case ImgDrawResult::BAD_IMAGE:
|
||||
case ImgDrawResult::BAD_ARGS:
|
||||
return container.forget();
|
||||
container.forget(aOutContainer);
|
||||
return entry->mLastDrawResult;
|
||||
case ImgDrawResult::NOT_READY:
|
||||
case ImgDrawResult::INCOMPLETE:
|
||||
case ImgDrawResult::TEMPORARY_ERROR:
|
||||
|
@ -223,7 +230,8 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
// Unused by GetFrameInternal
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!");
|
||||
return container.forget();
|
||||
container.forget(aOutContainer);
|
||||
return entry->mLastDrawResult;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -245,7 +253,8 @@ ImageResource::GetImageContainerImpl(LayerManager* aManager,
|
|||
|
||||
SetCurrentImage(container, surface, true);
|
||||
entry->mLastDrawResult = drawResult;
|
||||
return container.forget();
|
||||
container.forget(aOutContainer);
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -353,18 +353,19 @@ protected:
|
|||
* the same as the size of the surface in the image container, but it is the
|
||||
* best effort estimate.
|
||||
*/
|
||||
virtual gfx::IntSize GetImageContainerSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags)
|
||||
virtual Tuple<ImgDrawResult, gfx::IntSize>
|
||||
GetImageContainerSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
return gfx::IntSize(0, 0);
|
||||
return MakeTuple(ImgDrawResult::NOT_SUPPORTED, gfx::IntSize(0, 0));
|
||||
}
|
||||
|
||||
already_AddRefed<layers::ImageContainer>
|
||||
GetImageContainerImpl(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags);
|
||||
ImgDrawResult GetImageContainerImpl(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aContainer);
|
||||
|
||||
void UpdateImageContainer();
|
||||
|
||||
|
|
|
@ -222,14 +222,15 @@ ImageWrapper::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
return mInnerImage->IsImageContainerAvailableAtSize(aManager, aSize, aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
ImageWrapper::GetImageContainerAtSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
ImageWrapper::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
return mInnerImage->GetImageContainerAtSize(aManager, aSize,
|
||||
aSVGContext, aFlags);
|
||||
return mInnerImage->GetImageContainerAtSize(aManager, aSize, aSVGContext,
|
||||
aFlags, aOutContainer);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
|
|
|
@ -51,6 +51,9 @@ namespace image {
|
|||
* permanent condition.
|
||||
*
|
||||
* BAD_ARGS: We failed to draw because bad arguments were passed to draw().
|
||||
*
|
||||
* NOT_SUPPORTED: The requested operation is not supported, but the image is
|
||||
* otherwise valid.
|
||||
*/
|
||||
enum class MOZ_MUST_USE_TYPE ImgDrawResult : uint8_t
|
||||
{
|
||||
|
@ -61,7 +64,8 @@ enum class MOZ_MUST_USE_TYPE ImgDrawResult : uint8_t
|
|||
NOT_READY,
|
||||
TEMPORARY_ERROR,
|
||||
BAD_IMAGE,
|
||||
BAD_ARGS
|
||||
BAD_ARGS,
|
||||
NOT_SUPPORTED
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -79,6 +83,11 @@ operator&(const ImgDrawResult aLeft, const ImgDrawResult aRight)
|
|||
return aRight;
|
||||
}
|
||||
|
||||
if (aLeft == ImgDrawResult::NOT_SUPPORTED ||
|
||||
aRight == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if ((aLeft == ImgDrawResult::BAD_IMAGE ||
|
||||
aLeft == ImgDrawResult::SUCCESS_NOT_COMPLETE) &&
|
||||
aRight != ImgDrawResult::SUCCESS &&
|
||||
|
|
|
@ -182,11 +182,12 @@ OrientedImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
return false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
OrientedImage::GetImageContainerAtSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
OrientedImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
// XXX(seth): We currently don't have a way of orienting the result of
|
||||
// GetImageContainer. We work around this by always returning null, but if it
|
||||
|
@ -195,11 +196,11 @@ OrientedImage::GetImageContainerAtSize(LayerManager* aManager,
|
|||
// that method for performance reasons.
|
||||
|
||||
if (mOrientation.IsIdentity()) {
|
||||
return InnerImage()->GetImageContainerAtSize(aManager, aSize,
|
||||
aSVGContext, aFlags);
|
||||
return InnerImage()->GetImageContainerAtSize(aManager, aSize, aSVGContext,
|
||||
aFlags, aOutContainer);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
struct MatrixBuilder
|
||||
|
|
|
@ -48,11 +48,12 @@ public:
|
|||
IsImageContainerAvailableAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
NS_IMETHOD_(already_AddRefed<layers::ImageContainer>)
|
||||
NS_IMETHOD_(ImgDrawResult)
|
||||
GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags) override;
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer) override;
|
||||
NS_IMETHOD_(ImgDrawResult) Draw(gfxContext* aContext,
|
||||
const nsIntSize& aSize,
|
||||
const ImageRegion& aRegion,
|
||||
|
|
|
@ -627,20 +627,34 @@ RasterImage::GetFrameInternal(const IntSize& aSize,
|
|||
return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize, std::move(surface));
|
||||
}
|
||||
|
||||
IntSize
|
||||
Tuple<ImgDrawResult, IntSize>
|
||||
RasterImage::GetImageContainerSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
|
||||
return IntSize(0, 0);
|
||||
if (!mHasSize) {
|
||||
return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
|
||||
}
|
||||
|
||||
if (aSize.IsEmpty()) {
|
||||
return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
|
||||
}
|
||||
|
||||
// We check the minimum size because while we support downscaling, we do not
|
||||
// support upscaling. If aSize > mSize, we will never give a larger surface
|
||||
// than mSize. If mSize > aSize, and mSize > maxTextureSize, we still want to
|
||||
// use image containers if aSize <= maxTextureSize.
|
||||
int32_t maxTextureSize = aManager->GetMaxTextureSize();
|
||||
if (min(mSize.width, aSize.width) > maxTextureSize ||
|
||||
min(mSize.height, aSize.height) > maxTextureSize) {
|
||||
return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
|
||||
}
|
||||
|
||||
if (!CanDownscaleDuringDecode(aSize, aFlags)) {
|
||||
return mSize;
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, mSize);
|
||||
}
|
||||
|
||||
return aSize;
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, aSize);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
|
@ -652,7 +666,15 @@ RasterImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
|
|||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
|
||||
{
|
||||
return GetImageContainerImpl(aManager, mSize, Nothing(), aFlags);
|
||||
RefPtr<ImageContainer> container;
|
||||
ImgDrawResult drawResult =
|
||||
GetImageContainerImpl(aManager, mSize, Nothing(), aFlags,
|
||||
getter_AddRefs(container));
|
||||
|
||||
// We silence the unused warning here because anything that needs the draw
|
||||
// result should be using GetImageContainerAtSize, not GetImageContainer.
|
||||
(void)drawResult;
|
||||
return container.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
|
@ -674,16 +696,18 @@ RasterImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
RasterImage::GetImageContainerAtSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
RasterImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
// We do not pass in the given SVG context because in theory it could differ
|
||||
// between calls, but actually have no impact on the actual contents of the
|
||||
// image container.
|
||||
return GetImageContainerImpl(aManager, aSize, Nothing(), aFlags);
|
||||
return GetImageContainerImpl(aManager, aSize, Nothing(),
|
||||
aFlags, aOutContainer);
|
||||
}
|
||||
|
||||
size_t
|
||||
|
|
|
@ -312,9 +312,10 @@ private:
|
|||
uint32_t aWhichFrame,
|
||||
uint32_t aFlags) override;
|
||||
|
||||
gfx::IntSize GetImageContainerSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
Tuple<ImgDrawResult, gfx::IntSize>
|
||||
GetImageContainerSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Decoding.
|
||||
|
|
|
@ -849,16 +849,31 @@ VectorImage::GetFrameInternal(const IntSize& aSize,
|
|||
}
|
||||
|
||||
//******************************************************************************
|
||||
IntSize
|
||||
Tuple<ImgDrawResult, IntSize>
|
||||
VectorImage::GetImageContainerSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
if (!IsImageContainerAvailableAtSize(aManager, aSize, aFlags)) {
|
||||
return IntSize(0, 0);
|
||||
if (mError) {
|
||||
return MakeTuple(ImgDrawResult::BAD_IMAGE, IntSize(0, 0));
|
||||
}
|
||||
|
||||
return aSize;
|
||||
if (!mIsFullyLoaded) {
|
||||
return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0));
|
||||
}
|
||||
|
||||
if (mHaveAnimations ||
|
||||
aManager->GetBackendType() != LayersBackend::LAYERS_WR) {
|
||||
return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0));
|
||||
}
|
||||
|
||||
// We don't need to check if the size is too big since we only support
|
||||
// WebRender backends.
|
||||
if (aSize.IsEmpty()) {
|
||||
return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0));
|
||||
}
|
||||
|
||||
return MakeTuple(ImgDrawResult::SUCCESS, aSize);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
|
@ -893,11 +908,12 @@ VectorImage::IsImageContainerAvailableAtSize(LayerManager* aManager,
|
|||
}
|
||||
|
||||
//******************************************************************************
|
||||
NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
|
||||
VectorImage::GetImageContainerAtSize(LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
NS_IMETHODIMP_(ImgDrawResult)
|
||||
VectorImage::GetImageContainerAtSize(layers::LayerManager* aManager,
|
||||
const gfx::IntSize& aSize,
|
||||
const Maybe<SVGImageContext>& aSVGContext,
|
||||
uint32_t aFlags)
|
||||
uint32_t aFlags,
|
||||
layers::ImageContainer** aOutContainer)
|
||||
{
|
||||
Maybe<SVGImageContext> newSVGContext;
|
||||
MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags);
|
||||
|
@ -910,7 +926,7 @@ VectorImage::GetImageContainerAtSize(LayerManager* aManager,
|
|||
FLAG_FORCE_PRESERVEASPECTRATIO_NONE);
|
||||
return GetImageContainerImpl(aManager, aSize,
|
||||
newSVGContext ? newSVGContext : aSVGContext,
|
||||
flags);
|
||||
flags, aOutContainer);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -87,9 +87,10 @@ private:
|
|||
uint32_t aWhichFrame,
|
||||
uint32_t aFlags) override;
|
||||
|
||||
IntSize GetImageContainerSize(layers::LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
Tuple<ImgDrawResult, IntSize>
|
||||
GetImageContainerSize(layers::LayerManager* aManager,
|
||||
const IntSize& aSize,
|
||||
uint32_t aFlags) override;
|
||||
|
||||
/// Attempt to find a matching cached surface in the SurfaceCache.
|
||||
already_AddRefed<SourceSurface>
|
||||
|
|
|
@ -57,6 +57,7 @@ native nsIntRectByVal(nsIntRect);
|
|||
native nsSize(nsSize);
|
||||
[ptr] native nsIFrame(nsIFrame);
|
||||
native TempRefImageContainer(already_AddRefed<mozilla::layers::ImageContainer>);
|
||||
[ptr] native ImageContainer(mozilla::layers::ImageContainer);
|
||||
[ref] native ImageRegion(mozilla::image::ImageRegion);
|
||||
[ptr] native LayerManager(mozilla::layers::LayerManager);
|
||||
native Orientation(mozilla::image::Orientation);
|
||||
|
@ -295,15 +296,6 @@ interface imgIContainer : nsISupports
|
|||
[noscript, notxpcom] TempRefImageContainer getImageContainer(in LayerManager aManager,
|
||||
in uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* @return true if getImageContainer() is expected to return a valid
|
||||
* ImageContainer when passed the given @Manager, @Size and @Flags
|
||||
* parameters.
|
||||
*/
|
||||
[noscript, notxpcom] boolean isImageContainerAvailableAtSize(in LayerManager aManager,
|
||||
[const] in nsIntSize aSize,
|
||||
in uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* Attempts to create an ImageContainer (and Image) containing the current
|
||||
* frame at the given size. Match the requested size is best effort; it's
|
||||
|
@ -322,13 +314,24 @@ interface imgIContainer : nsISupports
|
|||
* @param aFlags Decoding / drawing flags (in other words, FLAG_* flags).
|
||||
* Currently only FLAG_SYNC_DECODE and FLAG_SYNC_DECODE_IF_FAST
|
||||
* are supported.
|
||||
* @return An ImageContainer for the current frame, or nullptr if one could
|
||||
* not be created.
|
||||
* @param aContainer Return value for ImageContainer for the current frame.
|
||||
* May be null depending on the draw result.
|
||||
* @return The draw result for the current frame.
|
||||
*/
|
||||
[noscript, notxpcom] TempRefImageContainer getImageContainerAtSize(in LayerManager aManager,
|
||||
[const] in nsIntSize aSize,
|
||||
[const] in MaybeSVGImageContext aSVGContext,
|
||||
in uint32_t aFlags);
|
||||
[noscript, notxpcom] ImgDrawResult getImageContainerAtSize(in LayerManager aManager,
|
||||
[const] in nsIntSize aSize,
|
||||
[const] in MaybeSVGImageContext aSVGContext,
|
||||
in uint32_t aFlags,
|
||||
out ImageContainer aOutContainer);
|
||||
|
||||
/**
|
||||
* @return true if getImageContainer() is expected to return a valid
|
||||
* ImageContainer when passed the given @Manager, @Size and @Flags
|
||||
* parameters.
|
||||
*/
|
||||
[noscript, notxpcom] boolean isImageContainerAvailableAtSize(in LayerManager aManager,
|
||||
[const] in nsIntSize aSize,
|
||||
in uint32_t aFlags);
|
||||
|
||||
/**
|
||||
* Draw the requested frame of this image onto the context specified.
|
||||
|
|
|
@ -68,14 +68,17 @@ TEST_F(ImageContainers, RasterImageContainer)
|
|||
EXPECT_EQ(testCase.mSize.height, containerSize.height);
|
||||
|
||||
// Upscaling should give the native size.
|
||||
ImgDrawResult drawResult;
|
||||
IntSize requestedSize = testCase.mSize;
|
||||
requestedSize.Scale(2, 2);
|
||||
RefPtr<layers::ImageContainer> upscaleContainer =
|
||||
image->GetImageContainerAtSize(layerManager,
|
||||
requestedSize,
|
||||
Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE |
|
||||
imgIContainer::FLAG_HIGH_QUALITY_SCALING);
|
||||
RefPtr<layers::ImageContainer> upscaleContainer;
|
||||
drawResult = image->GetImageContainerAtSize(layerManager,
|
||||
requestedSize,
|
||||
Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE |
|
||||
imgIContainer::FLAG_HIGH_QUALITY_SCALING,
|
||||
getter_AddRefs(upscaleContainer));
|
||||
EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
|
||||
ASSERT_TRUE(upscaleContainer != nullptr);
|
||||
containerSize = upscaleContainer->GetCurrentSize();
|
||||
EXPECT_EQ(testCase.mSize.width, containerSize.width);
|
||||
|
@ -85,21 +88,26 @@ TEST_F(ImageContainers, RasterImageContainer)
|
|||
requestedSize = testCase.mSize;
|
||||
requestedSize.width /= 2;
|
||||
requestedSize.height /= 2;
|
||||
RefPtr<layers::ImageContainer> downscaleContainer =
|
||||
image->GetImageContainerAtSize(layerManager,
|
||||
requestedSize,
|
||||
Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE |
|
||||
imgIContainer::FLAG_HIGH_QUALITY_SCALING);
|
||||
RefPtr<layers::ImageContainer> downscaleContainer;
|
||||
drawResult = image->GetImageContainerAtSize(layerManager,
|
||||
requestedSize,
|
||||
Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE |
|
||||
imgIContainer::FLAG_HIGH_QUALITY_SCALING,
|
||||
getter_AddRefs(downscaleContainer));
|
||||
EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
|
||||
ASSERT_TRUE(downscaleContainer != nullptr);
|
||||
containerSize = downscaleContainer->GetCurrentSize();
|
||||
EXPECT_EQ(requestedSize.width, containerSize.width);
|
||||
EXPECT_EQ(requestedSize.height, containerSize.height);
|
||||
|
||||
// Get at native size again. Should give same container.
|
||||
RefPtr<layers::ImageContainer> againContainer =
|
||||
image->GetImageContainerAtSize(layerManager,
|
||||
testCase.mSize,
|
||||
Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE);
|
||||
RefPtr<layers::ImageContainer> againContainer;
|
||||
drawResult = image->GetImageContainerAtSize(layerManager,
|
||||
testCase.mSize,
|
||||
Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE,
|
||||
getter_AddRefs(againContainer));
|
||||
EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS);
|
||||
ASSERT_EQ(nativeContainer.get(), againContainer.get());
|
||||
}
|
||||
|
|
|
@ -2660,8 +2660,11 @@ BytecodeEmitter::emitSwitch(SwitchStatement* switchStmt)
|
|||
|
||||
NumericLiteral* literal = &caseValue->as<NumericLiteral>();
|
||||
#ifdef DEBUG
|
||||
// Use NumberEqualsInt32 here because switches compare using
|
||||
// strict equality, which will equate -0 and +0. In contrast
|
||||
// NumberIsInt32 would return false for -0.
|
||||
int32_t v;
|
||||
MOZ_ASSERT(mozilla::NumberIsInt32(literal->value(), &v));
|
||||
MOZ_ASSERT(mozilla::NumberEqualsInt32(literal->value(), &v));
|
||||
#endif
|
||||
int32_t i = int32_t(literal->value());
|
||||
|
||||
|
|
|
@ -1838,6 +1838,16 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
|
|||
|
||||
masm.bind(&nonEmpty);
|
||||
|
||||
// Complete matches use the base string.
|
||||
Label nonBaseStringMatch;
|
||||
masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch);
|
||||
masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()), temp1_,
|
||||
&nonBaseStringMatch);
|
||||
masm.movePtr(base, string_);
|
||||
masm.jump(&done);
|
||||
|
||||
masm.bind(&nonBaseStringMatch);
|
||||
|
||||
Label notInline;
|
||||
|
||||
int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
|
||||
|
@ -1852,6 +1862,25 @@ CreateDependentString::generate(MacroAssembler& masm, const JSAtomState& names,
|
|||
? JSThinInlineString::MAX_LENGTH_LATIN1
|
||||
: JSThinInlineString::MAX_LENGTH_TWO_BYTE;
|
||||
masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), &fatInline);
|
||||
if (encoding_ == CharEncoding::Latin1) {
|
||||
// One character Latin-1 strings can be loaded directly from the
|
||||
// static strings table.
|
||||
Label thinInline;
|
||||
masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline);
|
||||
{
|
||||
static_assert(StaticStrings::UNIT_STATIC_LIMIT -1 == JSString::MAX_LATIN1_CHAR,
|
||||
"Latin-1 strings can be loaded from static strings");
|
||||
|
||||
masm.loadStringChars(base, temp1_, encoding_);
|
||||
masm.loadChar(temp1_, temp2_, temp1_, encoding_);
|
||||
|
||||
masm.movePtr(ImmPtr(&runtime->staticStrings().unitStaticTable), string_);
|
||||
masm.loadPtr(BaseIndex(string_, temp1_, ScalePointer), string_);
|
||||
|
||||
masm.jump(&done);
|
||||
}
|
||||
masm.bind(&thinInline);
|
||||
}
|
||||
{
|
||||
newGCString(FallbackKind::InlineString);
|
||||
masm.jump(&stringAllocated);
|
||||
|
|
|
@ -177,14 +177,21 @@ nsDisplayFieldSetBorder::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder
|
|||
rect = nsRect(offset, frame->GetRect().Size());
|
||||
}
|
||||
|
||||
return nsCSSRendering::CreateWebRenderCommandsForBorder(this,
|
||||
mFrame,
|
||||
rect,
|
||||
aBuilder,
|
||||
aResources,
|
||||
aSc,
|
||||
aManager,
|
||||
aDisplayListBuilder);
|
||||
ImgDrawResult drawResult =
|
||||
nsCSSRendering::CreateWebRenderCommandsForBorder(this,
|
||||
mFrame,
|
||||
rect,
|
||||
aBuilder,
|
||||
aResources,
|
||||
aSc,
|
||||
aManager,
|
||||
aDisplayListBuilder);
|
||||
if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, drawResult);
|
||||
return true;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
|
@ -241,7 +241,7 @@ public:
|
|||
MOZ_ASSERT(IsTextType());
|
||||
}
|
||||
|
||||
bool
|
||||
ImgDrawResult
|
||||
CreateWebRenderCommands(nsDisplayItem* aItem,
|
||||
wr::DisplayListBuilder& aBuilder,
|
||||
wr::IpcResourceUpdateQueue& aResources,
|
||||
|
@ -291,7 +291,7 @@ public:
|
|||
IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags);
|
||||
|
||||
private:
|
||||
bool
|
||||
ImgDrawResult
|
||||
CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
|
||||
wr::DisplayListBuilder& aBuilder,
|
||||
wr::IpcResourceUpdateQueue& aResources,
|
||||
|
@ -349,7 +349,7 @@ private:
|
|||
int32_t mListStyleType;
|
||||
};
|
||||
|
||||
bool
|
||||
ImgDrawResult
|
||||
BulletRenderer::CreateWebRenderCommands(nsDisplayItem* aItem,
|
||||
wr::DisplayListBuilder& aBuilder,
|
||||
wr::IpcResourceUpdateQueue& aResources,
|
||||
|
@ -360,14 +360,19 @@ BulletRenderer::CreateWebRenderCommands(nsDisplayItem* aItem,
|
|||
if (IsImageType()) {
|
||||
return CreateWebRenderCommandsForImage(aItem, aBuilder, aResources,
|
||||
aSc, aManager, aDisplayListBuilder);
|
||||
} else if (IsPathType()) {
|
||||
return CreateWebRenderCommandsForPath(aItem, aBuilder, aResources,
|
||||
aSc, aManager, aDisplayListBuilder);
|
||||
}
|
||||
|
||||
bool success;
|
||||
if (IsPathType()) {
|
||||
success = CreateWebRenderCommandsForPath(aItem, aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder);
|
||||
} else {
|
||||
MOZ_ASSERT(IsTextType());
|
||||
return CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder);
|
||||
success = CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder);
|
||||
}
|
||||
|
||||
return success ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
ImgDrawResult
|
||||
|
@ -454,7 +459,7 @@ BulletRenderer::IsImageContainerAvailable(layers::LayerManager* aManager, uint32
|
|||
return mImage->IsImageContainerAvailable(aManager, aFlags);
|
||||
}
|
||||
|
||||
bool
|
||||
ImgDrawResult
|
||||
BulletRenderer::CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
|
||||
wr::DisplayListBuilder& aBuilder,
|
||||
wr::IpcResourceUpdateQueue& aResources,
|
||||
|
@ -479,10 +484,13 @@ BulletRenderer::CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
|
|||
gfx::IntSize decodeSize =
|
||||
nsLayoutUtils::ComputeImageContainerDrawingParameters(mImage, aItem->Frame(), destRect,
|
||||
aSc, flags, svgContext);
|
||||
RefPtr<layers::ImageContainer> container =
|
||||
mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
|
||||
|
||||
RefPtr<layers::ImageContainer> container;
|
||||
ImgDrawResult drawResult =
|
||||
mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext,
|
||||
flags, getter_AddRefs(container));
|
||||
if (!container) {
|
||||
return false;
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
|
||||
|
@ -491,7 +499,7 @@ BulletRenderer::CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
|
|||
Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
|
||||
aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing());
|
||||
if (key.isNothing()) {
|
||||
return true; // Nothing to do
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
wr::LayoutRect dest = wr::ToRoundedLayoutRect(destRect);
|
||||
|
@ -499,7 +507,7 @@ BulletRenderer::CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
|
|||
aBuilder.PushImage(
|
||||
dest, dest, !aItem->BackfaceIsHidden(), rendering, key.value());
|
||||
|
||||
return true;
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -665,8 +673,15 @@ nsDisplayBullet::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
|
|||
return false;
|
||||
}
|
||||
|
||||
return br->CreateWebRenderCommands(this, aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder);
|
||||
ImgDrawResult drawResult =
|
||||
br->CreateWebRenderCommands(this, aBuilder, aResources, aSc,
|
||||
aManager, aDisplayListBuilder);
|
||||
if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsDisplayBulletGeometry::UpdateDrawResult(this, drawResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
|
||||
|
|
|
@ -1814,16 +1814,60 @@ nsDisplayImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilde
|
|||
IntSize decodeSize =
|
||||
nsLayoutUtils::ComputeImageContainerDrawingParameters(mImage, mFrame, destRect,
|
||||
aSc, flags, svgContext);
|
||||
RefPtr<ImageContainer> container =
|
||||
mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
|
||||
if (!container) {
|
||||
return false;
|
||||
|
||||
RefPtr<layers::ImageContainer> container;
|
||||
ImgDrawResult drawResult =
|
||||
mImage->GetImageContainerAtSize(aManager, decodeSize, svgContext,
|
||||
flags, getter_AddRefs(container));
|
||||
|
||||
// While we got a container, it may not contain a fully decoded surface. If
|
||||
// that is the case, and we have an image we were previously displaying which
|
||||
// has a fully decoded surface, then we should prefer the previous image.
|
||||
bool updatePrevImage = false;
|
||||
switch (drawResult) {
|
||||
case ImgDrawResult::NOT_READY:
|
||||
case ImgDrawResult::INCOMPLETE:
|
||||
case ImgDrawResult::TEMPORARY_ERROR:
|
||||
if (mPrevImage && mPrevImage != mImage) {
|
||||
RefPtr<ImageContainer> prevContainer;
|
||||
drawResult = mPrevImage->GetImageContainerAtSize(aManager, decodeSize,
|
||||
svgContext, flags,
|
||||
getter_AddRefs(prevContainer));
|
||||
if (prevContainer && drawResult == ImgDrawResult::SUCCESS) {
|
||||
container = std::move(prevContainer);
|
||||
break;
|
||||
}
|
||||
|
||||
// Previous image was unusable; we can forget about it.
|
||||
updatePrevImage = true;
|
||||
}
|
||||
break;
|
||||
case ImgDrawResult::NOT_SUPPORTED:
|
||||
return false;
|
||||
default:
|
||||
updatePrevImage = mPrevImage != mImage;
|
||||
break;
|
||||
}
|
||||
|
||||
// The previous image was not used, and is different from the current image.
|
||||
// We should forget about it. We need to update the frame as well because the
|
||||
// display item may get recreated.
|
||||
if (updatePrevImage) {
|
||||
mPrevImage = mImage;
|
||||
if (mFrame->IsImageFrame()) {
|
||||
nsImageFrame* f = static_cast<nsImageFrame*>(mFrame);
|
||||
f->mPrevImage = f->mImage;
|
||||
}
|
||||
}
|
||||
|
||||
// If the image container is empty, we don't want to fallback. Any other
|
||||
// failure will be due to resource constraints and fallback is unlikely to
|
||||
// help us. Hence we can ignore the return value from PushImage.
|
||||
aManager->CommandBuilder().PushImage(this, container, aBuilder, aResources, aSc, destRect);
|
||||
if (container) {
|
||||
aManager->CommandBuilder().PushImage(this, container, aBuilder, aResources, aSc, destRect);
|
||||
}
|
||||
|
||||
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, drawResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -759,7 +759,7 @@ nsCSSRendering::CreateBorderRenderer(nsPresContext* aPresContext,
|
|||
aSkipSides);
|
||||
}
|
||||
|
||||
bool
|
||||
ImgDrawResult
|
||||
nsCSSRendering::CreateWebRenderCommandsForBorder(
|
||||
nsDisplayItem* aItem,
|
||||
nsIFrame* aForFrame,
|
||||
|
@ -783,12 +783,12 @@ nsCSSRendering::CreateWebRenderCommandsForBorder(
|
|||
&borderIsEmpty,
|
||||
aForFrame->GetSkipSides());
|
||||
if (borderIsEmpty) {
|
||||
return true;
|
||||
return ImgDrawResult::SUCCESS;
|
||||
}
|
||||
|
||||
if (br) {
|
||||
br->CreateWebRenderCommands(aItem, aBuilder, aResources, aSc);
|
||||
return true;
|
||||
return ImgDrawResult::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -798,7 +798,7 @@ nsCSSRendering::CreateWebRenderCommandsForBorder(
|
|||
|
||||
// Filter out unsupported image/border types
|
||||
if (!image) {
|
||||
return false;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// All this code bitrotted too much (but is almost right); disabled for now.
|
||||
|
@ -807,14 +807,14 @@ nsCSSRendering::CreateWebRenderCommandsForBorder(
|
|||
// FIXME(1409774): fix this: image->GetType() == eStyleImageType_Gradient;
|
||||
|
||||
if (!imageTypeSupported) {
|
||||
return false;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (styleBorder->mBorderImageRepeatH == StyleBorderImageRepeat::Round ||
|
||||
styleBorder->mBorderImageRepeatH == StyleBorderImageRepeat::Space ||
|
||||
styleBorder->mBorderImageRepeatV == StyleBorderImageRepeat::Round ||
|
||||
styleBorder->mBorderImageRepeatV == StyleBorderImageRepeat::Space) {
|
||||
return false;
|
||||
return ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
|
@ -835,18 +835,11 @@ nsCSSRendering::CreateWebRenderCommandsForBorder(
|
|||
&result);
|
||||
|
||||
if (!bir) {
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (image->GetType() == eStyleImageType_Image &&
|
||||
!bir->mImageRenderer.IsImageContainerAvailable(aManager, flags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bir->CreateWebRenderCommands(
|
||||
return bir->CreateWebRenderCommands(
|
||||
aItem, aForFrame, aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsCSSBorderRenderer
|
||||
|
|
|
@ -233,7 +233,7 @@ struct nsCSSRendering
|
|||
const nsRect& aBorderArea,
|
||||
mozilla::ComputedStyle* aComputedStyle);
|
||||
|
||||
static bool CreateWebRenderCommandsForBorder(
|
||||
static ImgDrawResult CreateWebRenderCommandsForBorder(
|
||||
nsDisplayItem* aItem,
|
||||
nsIFrame* aForFrame,
|
||||
const nsRect& aBorderArea,
|
||||
|
|
|
@ -3801,7 +3801,7 @@ nsCSSBorderImageRenderer::DrawBorderImage(nsPresContext* aPresContext,
|
|||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
ImgDrawResult
|
||||
nsCSSBorderImageRenderer::CreateWebRenderCommands(
|
||||
nsDisplayItem* aItem,
|
||||
nsIFrame* aForFrame,
|
||||
|
@ -3812,7 +3812,7 @@ nsCSSBorderImageRenderer::CreateWebRenderCommands(
|
|||
nsDisplayListBuilder* aDisplayListBuilder)
|
||||
{
|
||||
if (!mImageRenderer.IsReady()) {
|
||||
return;
|
||||
return ImgDrawResult::NOT_READY;
|
||||
}
|
||||
|
||||
float widths[4];
|
||||
|
@ -3838,6 +3838,7 @@ nsCSSBorderImageRenderer::CreateWebRenderCommands(
|
|||
clip = wr::ToRoundedLayoutRect(clipRect);
|
||||
}
|
||||
|
||||
ImgDrawResult drawResult = ImgDrawResult::SUCCESS;
|
||||
switch (mImageRenderer.GetType()) {
|
||||
case eStyleImageType_Image: {
|
||||
uint32_t flags = imgIContainer::FLAG_ASYNC_NOTIFY;
|
||||
|
@ -3853,10 +3854,12 @@ nsCSSBorderImageRenderer::CreateWebRenderCommands(
|
|||
gfx::IntSize decodeSize =
|
||||
nsLayoutUtils::ComputeImageContainerDrawingParameters(
|
||||
img, aForFrame, destRect, aSc, flags, svgContext);
|
||||
RefPtr<layers::ImageContainer> container =
|
||||
img->GetImageContainerAtSize(aManager, decodeSize, svgContext, flags);
|
||||
|
||||
RefPtr<layers::ImageContainer> container;
|
||||
drawResult = img->GetImageContainerAtSize(aManager, decodeSize, svgContext,
|
||||
flags, getter_AddRefs(container));
|
||||
if (!container) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
|
||||
|
@ -3872,7 +3875,7 @@ nsCSSBorderImageRenderer::CreateWebRenderCommands(
|
|||
size,
|
||||
Nothing());
|
||||
if (key.isNothing()) {
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
aBuilder.PushBorderImage(
|
||||
|
@ -3937,7 +3940,10 @@ nsCSSBorderImageRenderer::CreateWebRenderCommands(
|
|||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unsupport border image type");
|
||||
drawResult = ImgDrawResult::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
nsCSSBorderImageRenderer::nsCSSBorderImageRenderer(
|
||||
|
|
|
@ -291,7 +291,7 @@ public:
|
|||
gfxContext& aRenderingContext,
|
||||
nsIFrame* aForFrame,
|
||||
const nsRect& aDirtyRect);
|
||||
void CreateWebRenderCommands(
|
||||
mozilla::image::ImgDrawResult CreateWebRenderCommands(
|
||||
nsDisplayItem* aItem,
|
||||
nsIFrame* aForFrame,
|
||||
mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
|
|
|
@ -4298,8 +4298,11 @@ nsDisplayBackgroundImage::CreateWebRenderCommands(
|
|||
ImgDrawResult result =
|
||||
nsCSSRendering::BuildWebRenderDisplayItemsForStyleImageLayer(
|
||||
params, aBuilder, aResources, aSc, aManager, this);
|
||||
nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
|
||||
if (result == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5537,14 +5540,22 @@ nsDisplayBorder::CreateWebRenderCommands(
|
|||
{
|
||||
nsRect rect = nsRect(ToReferenceFrame(), mFrame->GetSize());
|
||||
|
||||
return nsCSSRendering::CreateWebRenderCommandsForBorder(this,
|
||||
mFrame,
|
||||
rect,
|
||||
aBuilder,
|
||||
aResources,
|
||||
aSc,
|
||||
aManager,
|
||||
aDisplayListBuilder);
|
||||
ImgDrawResult drawResult =
|
||||
nsCSSRendering::CreateWebRenderCommandsForBorder(this,
|
||||
mFrame,
|
||||
rect,
|
||||
aBuilder,
|
||||
aResources,
|
||||
aSc,
|
||||
aManager,
|
||||
aDisplayListBuilder);
|
||||
|
||||
if (drawResult == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsDisplayBorderGeometry::UpdateDrawResult(this, drawResult);
|
||||
return true;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
|
@ -125,6 +125,9 @@ public:
|
|||
static void UpdateDrawResult(nsDisplayItem* aItem,
|
||||
mozilla::image::ImgDrawResult aResult)
|
||||
{
|
||||
MOZ_ASSERT(aResult != mozilla::image::ImgDrawResult::NOT_SUPPORTED,
|
||||
"ImgDrawResult::NOT_SUPPORTED should be handled already!");
|
||||
|
||||
auto lastGeometry =
|
||||
static_cast<T*>(mozilla::FrameLayerBuilder::GetMostRecentGeometry(aItem));
|
||||
if (lastGeometry) {
|
||||
|
|
|
@ -602,6 +602,7 @@ nsImageRenderer::BuildWebRenderDisplayItems(
|
|||
return ImgDrawResult::SUCCESS;
|
||||
}
|
||||
|
||||
ImgDrawResult drawResult = ImgDrawResult::SUCCESS;
|
||||
switch (mType) {
|
||||
case eStyleImageType_Gradient: {
|
||||
nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
|
||||
|
@ -641,12 +642,13 @@ nsImageRenderer::BuildWebRenderDisplayItems(
|
|||
aSc,
|
||||
containerFlags,
|
||||
svgContext);
|
||||
RefPtr<layers::ImageContainer> container =
|
||||
mImageContainer->GetImageContainerAtSize(
|
||||
aManager, decodeSize, svgContext, containerFlags);
|
||||
|
||||
RefPtr<layers::ImageContainer> container;
|
||||
drawResult = mImageContainer->GetImageContainerAtSize(aManager, decodeSize, svgContext,
|
||||
containerFlags, getter_AddRefs(container));
|
||||
if (!container) {
|
||||
NS_WARNING("Failed to get image container");
|
||||
return ImgDrawResult::NOT_READY;
|
||||
break;
|
||||
}
|
||||
|
||||
mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
|
||||
|
@ -663,7 +665,7 @@ nsImageRenderer::BuildWebRenderDisplayItems(
|
|||
Nothing());
|
||||
|
||||
if (key.isNothing()) {
|
||||
return ImgDrawResult::NOT_READY;
|
||||
break;
|
||||
}
|
||||
|
||||
nsPoint firstTilePos = nsLayoutUtils::GetBackgroundFirstTilePos(
|
||||
|
@ -721,8 +723,10 @@ nsImageRenderer::BuildWebRenderDisplayItems(
|
|||
break;
|
||||
}
|
||||
|
||||
return mImage->IsComplete() ? ImgDrawResult::SUCCESS
|
||||
: ImgDrawResult::SUCCESS_NOT_COMPLETE;
|
||||
if (!mImage->IsComplete() && drawResult == ImgDrawResult::SUCCESS) {
|
||||
return ImgDrawResult::SUCCESS_NOT_COMPLETE;
|
||||
}
|
||||
return drawResult;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxDrawable>
|
||||
|
|
|
@ -418,7 +418,7 @@ nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
|
|||
hasSubRect ? &mSubRect : nullptr);
|
||||
}
|
||||
|
||||
Maybe<ImgDrawResult>
|
||||
ImgDrawResult
|
||||
nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const StackingContextHelper& aSc,
|
||||
|
@ -434,7 +434,7 @@ nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuild
|
|||
anchorPoint,
|
||||
dest);
|
||||
if (!imgCon) {
|
||||
return Nothing();
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t containerFlags = imgIContainer::FLAG_ASYNC_NOTIFY;
|
||||
|
@ -452,11 +452,13 @@ nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuild
|
|||
gfx::IntSize decodeSize =
|
||||
nsLayoutUtils::ComputeImageContainerDrawingParameters(imgCon, aItem->Frame(), fillRect,
|
||||
aSc, containerFlags, svgContext);
|
||||
RefPtr<layers::ImageContainer> container =
|
||||
imgCon->GetImageContainerAtSize(aManager, decodeSize, svgContext, containerFlags);
|
||||
|
||||
RefPtr<layers::ImageContainer> container;
|
||||
result = imgCon->GetImageContainerAtSize(aManager, decodeSize, svgContext,
|
||||
containerFlags, getter_AddRefs(container));
|
||||
if (!container) {
|
||||
NS_WARNING("Failed to get image container");
|
||||
return Nothing();
|
||||
return result;
|
||||
}
|
||||
|
||||
mozilla::wr::ImageRendering rendering = wr::ToImageRendering(
|
||||
|
@ -465,7 +467,7 @@ nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuild
|
|||
Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(
|
||||
aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing());
|
||||
if (key.isNothing()) {
|
||||
return Some(ImgDrawResult::NOT_READY);
|
||||
return result;
|
||||
}
|
||||
wr::LayoutRect fill = wr::ToRoundedLayoutRect(fillRect);
|
||||
|
||||
|
@ -478,7 +480,7 @@ nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuild
|
|||
rendering,
|
||||
key.value());
|
||||
|
||||
return Some(ImgDrawResult::SUCCESS);
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRect
|
||||
|
@ -569,13 +571,13 @@ nsDisplayXULImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBui
|
|||
flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
|
||||
}
|
||||
|
||||
Maybe<ImgDrawResult> result = imageFrame->
|
||||
ImgDrawResult result = imageFrame->
|
||||
CreateWebRenderCommands(aBuilder, aResources, aSc, aManager, this, ToReferenceFrame(), flags);
|
||||
if (!result) {
|
||||
if (result == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, *result);
|
||||
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -96,13 +96,13 @@ public:
|
|||
const nsRect& aDirtyRect,
|
||||
nsPoint aPt, uint32_t aFlags);
|
||||
|
||||
Maybe<ImgDrawResult> CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::WebRenderLayerManager* aManager,
|
||||
nsDisplayItem* aItem,
|
||||
nsPoint aPt,
|
||||
uint32_t aFlags);
|
||||
ImgDrawResult CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
|
||||
mozilla::wr::IpcResourceUpdateQueue& aResources,
|
||||
const mozilla::layers::StackingContextHelper& aSc,
|
||||
mozilla::layers::WebRenderLayerManager* aManager,
|
||||
nsDisplayItem* aItem,
|
||||
nsPoint aPt,
|
||||
uint32_t aFlags);
|
||||
|
||||
bool CanOptimizeToImageLayer();
|
||||
|
||||
|
|
24
mfbt/Maybe.h
24
mfbt/Maybe.h
|
@ -22,8 +22,6 @@
|
|||
#include <ostream>
|
||||
#include <type_traits>
|
||||
|
||||
class nsCycleCollectionTraversalCallback;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct Nothing { };
|
||||
|
@ -715,28 +713,6 @@ operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
|
|||
return !(aLHS < aRHS);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
||||
Maybe<T>& aMaybe,
|
||||
const char* aName,
|
||||
uint32_t aFlags = 0)
|
||||
{
|
||||
if (aMaybe.isSome()) {
|
||||
ImplCycleCollectionTraverse(aCallback, aMaybe.ref(), aName, aFlags);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
ImplCycleCollectionUnlink(Maybe<T>& aMaybe)
|
||||
{
|
||||
if (aMaybe.isSome()) {
|
||||
ImplCycleCollectionUnlink(aMaybe.ref());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_Maybe_h */
|
||||
|
|
|
@ -23,7 +23,7 @@ class GeckoViewNavigationContent extends GeckoViewContentModule {
|
|||
where=${aWhere} flags=${aFlags}`;
|
||||
|
||||
if (!this.enabled) {
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Remove this when we have a sensible error API.
|
||||
|
|
|
@ -6,6 +6,7 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ActorManagerParent: "resource://gre/modules/ActorManagerParent.jsm",
|
||||
EventDispatcher: "resource://gre/modules/Messaging.jsm",
|
||||
FileSource: "resource://gre/modules/L10nRegistry.jsm",
|
||||
GeckoViewTelemetryController: "resource://gre/modules/GeckoViewTelemetryController.jsm",
|
||||
GeckoViewUtils: "resource://gre/modules/GeckoViewUtils.jsm",
|
||||
|
@ -121,6 +122,11 @@ GeckoViewStartup.prototype = {
|
|||
let locales = Services.locale.getPackagedLocales();
|
||||
const greSource = new FileSource("toolkit", locales, "resource://gre/localization/{locale}/");
|
||||
L10nRegistry.registerSource(greSource);
|
||||
|
||||
// Listen for global EventDispatcher messages
|
||||
EventDispatcher.instance.registerListener(
|
||||
(aEvent, aData, aCallback) => Services.locale.setRequestedLocales([aData.languageTag]),
|
||||
"GeckoView:SetLocale");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
package org.mozilla.geckoview.test
|
||||
|
||||
import org.mozilla.geckoview.GeckoSession
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.ReuseSession
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.WithDevToolsAPI
|
||||
|
||||
import android.support.test.filters.MediumTest
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@MediumTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@WithDevToolsAPI
|
||||
class LocaleTest : BaseSessionTest() {
|
||||
|
||||
@Test fun setLocale() {
|
||||
sessionRule.runtime.getSettings().setLocale("en-GB");
|
||||
|
||||
val index = sessionRule.waitForChromeJS(String.format(
|
||||
"(function() {" +
|
||||
" return ChromeUtils.import('resource://gre/modules/Services.jsm', {})" +
|
||||
" .Services.locale.getRequestedLocales().indexOf('en-GB');" +
|
||||
"})()")) as Double;
|
||||
|
||||
assertThat("Requested locale is found", index, greaterThanOrEqualTo(0.0));
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import android.support.test.filters.MediumTest
|
|||
import android.support.test.runner.AndroidJUnit4
|
||||
import org.hamcrest.Matchers.*
|
||||
import org.junit.Assume.assumeThat
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
|
@ -201,6 +202,7 @@ class NavigationDelegateTest : BaseSessionTest() {
|
|||
})
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test fun redirectLoad() {
|
||||
val redirectUri = if (sessionRule.env.isAutomation) {
|
||||
"http://example.org/tests/robocop/robocop_blank_02.html"
|
||||
|
|
|
@ -79,6 +79,7 @@ class ProgressDelegateTest : BaseSessionTest() {
|
|||
})
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test fun multipleLoads() {
|
||||
sessionRule.session.loadUri(UNKNOWN_HOST_URI)
|
||||
sessionRule.session.loadTestPath(HELLO_HTML_PATH)
|
||||
|
|
|
@ -18,6 +18,8 @@ import android.support.annotation.IntDef;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.util.GeckoBundle;
|
||||
import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
|
||||
|
||||
public final class GeckoRuntimeSettings implements Parcelable {
|
||||
|
@ -291,6 +293,17 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
mSettings.mCrashHandler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale.
|
||||
*
|
||||
* @param languageTag The locale code in Gecko format ("en" or "en-US").
|
||||
* @return The builder instance.
|
||||
*/
|
||||
public @NonNull Builder locale(String languageTag) {
|
||||
mSettings.mLocale = languageTag;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/* package */ GeckoRuntime runtime;
|
||||
|
@ -360,6 +373,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
/* package */ int mScreenWidthOverride;
|
||||
/* package */ int mScreenHeightOverride;
|
||||
/* package */ Class<? extends Service> mCrashHandler;
|
||||
/* package */ String mLocale;
|
||||
|
||||
private final Pref<?>[] mPrefs = new Pref<?>[] {
|
||||
mCookieBehavior, mCookieLifetime, mConsoleOutput,
|
||||
|
@ -402,9 +416,11 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
mScreenWidthOverride = settings.mScreenWidthOverride;
|
||||
mScreenHeightOverride = settings.mScreenHeightOverride;
|
||||
mCrashHandler = settings.mCrashHandler;
|
||||
mLocale = settings.mLocale;
|
||||
}
|
||||
|
||||
/* package */ void flush() {
|
||||
flushLocale();
|
||||
for (final Pref<?> pref: mPrefs) {
|
||||
pref.flush();
|
||||
}
|
||||
|
@ -545,6 +561,29 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locale code in Gecko format ("en" or "en-US").
|
||||
*/
|
||||
public String getLocale() {
|
||||
return mLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale.
|
||||
*
|
||||
* @param languageTag The locale code in Gecko format ("en-US").
|
||||
*/
|
||||
public void setLocale(String languageTag) {
|
||||
mLocale = languageTag;
|
||||
flushLocale();
|
||||
}
|
||||
|
||||
private void flushLocale() {
|
||||
final GeckoBundle data = new GeckoBundle(1);
|
||||
data.putString("languageTag", mLocale);
|
||||
EventDispatcher.getInstance().dispatch("GeckoView:SetLocale", data);
|
||||
}
|
||||
|
||||
// Sync values with nsICookieService.idl.
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({ COOKIE_ACCEPT_ALL, COOKIE_ACCEPT_FIRST_PARTY,
|
||||
|
@ -753,6 +792,7 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
out.writeInt(mScreenWidthOverride);
|
||||
out.writeInt(mScreenHeightOverride);
|
||||
out.writeString(mCrashHandler != null ? mCrashHandler.getName() : null);
|
||||
out.writeString(mLocale);
|
||||
}
|
||||
|
||||
// AIDL code may call readFromParcel even though it's not part of Parcelable.
|
||||
|
@ -785,6 +825,8 @@ public final class GeckoRuntimeSettings implements Parcelable {
|
|||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
}
|
||||
|
||||
mLocale = source.readString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GeckoRuntimeSettings> CREATOR
|
||||
|
|
|
@ -150,24 +150,13 @@ class GeckoViewNavigation extends GeckoViewModule {
|
|||
return browser || null;
|
||||
}
|
||||
|
||||
isURIHandled(aUri, aWhere, aFlags) {
|
||||
debug `isURIHandled: uri=${aUri} where=${aWhere} flags=${aFlags}`;
|
||||
|
||||
let handled = undefined;
|
||||
LoadURIDelegate.load(this.window, this.eventDispatcher, aUri, aWhere, aFlags).then((response) => {
|
||||
handled = response;
|
||||
});
|
||||
|
||||
Services.tm.spinEventLoopUntil(() => this.window.closed || handled !== undefined);
|
||||
return handled;
|
||||
}
|
||||
|
||||
// nsIBrowserDOMWindow.
|
||||
createContentWindow(aUri, aOpener, aWhere, aFlags, aTriggeringPrincipal) {
|
||||
debug `createContentWindow: uri=${aUri && aUri.spec}
|
||||
where=${aWhere} flags=${aFlags}`;
|
||||
|
||||
if (this.isURIHandled(aUri, aWhere, aFlags)) {
|
||||
if (LoadURIDelegate.load(this.window, this.eventDispatcher,
|
||||
aUri, aWhere, aFlags)) {
|
||||
// The app has handled the load, abort open-window handling.
|
||||
Components.returnCode = Cr.NS_ERROR_ABORT;
|
||||
return null;
|
||||
|
@ -190,7 +179,8 @@ class GeckoViewNavigation extends GeckoViewModule {
|
|||
nextTabParentId=${aNextTabParentId}
|
||||
name=${aName}`;
|
||||
|
||||
if (this.isURIHandled(aUri, aWhere, aFlags)) {
|
||||
if (LoadURIDelegate.load(this.window, this.eventDispatcher,
|
||||
aUri, aWhere, aFlags)) {
|
||||
// The app has handled the load, abort open-window handling.
|
||||
Components.returnCode = Cr.NS_ERROR_ABORT;
|
||||
return null;
|
||||
|
@ -210,7 +200,8 @@ class GeckoViewNavigation extends GeckoViewModule {
|
|||
debug `handleOpenUri: uri=${aUri && aUri.spec}
|
||||
where=${aWhere} flags=${aFlags}`;
|
||||
|
||||
if (this.isURIHandled(aUri, aWhere, aFlags)) {
|
||||
if (LoadURIDelegate.load(this.window, this.eventDispatcher,
|
||||
aUri, aWhere, aFlags)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ const LoadURIDelegate = {
|
|||
// Return whether the loading has been handled.
|
||||
load: function(aWindow, aEventDispatcher, aUri, aWhere, aFlags) {
|
||||
if (!aWindow) {
|
||||
return Promise.resolve(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
const message = {
|
||||
|
@ -30,7 +30,18 @@ const LoadURIDelegate = {
|
|||
flags: aFlags
|
||||
};
|
||||
|
||||
return aEventDispatcher.sendRequestForResult(message).then((response) => response || false).catch(() => false);
|
||||
let handled = undefined;
|
||||
aEventDispatcher.sendRequestForResult(message).then(response => {
|
||||
handled = response;
|
||||
}, () => {
|
||||
// There was an error or listener was not registered in GeckoSession,
|
||||
// treat as unhandled.
|
||||
handled = false;
|
||||
});
|
||||
Services.tm.spinEventLoopUntil(() =>
|
||||
aWindow.closed || handled !== undefined);
|
||||
|
||||
return handled || false;
|
||||
},
|
||||
|
||||
handleLoadError: function(aWindow, aEventDispatcher, aUri, aError,
|
||||
|
|
|
@ -52,13 +52,6 @@ interface nsIChannelEventSink : nsISupports
|
|||
*/
|
||||
const unsigned long REDIRECT_STS_UPGRADE = 1 << 3;
|
||||
|
||||
/**
|
||||
* This redirect has already been presented to the nsILoadURIDelegate
|
||||
* for possible handling; if this flag is set we may safely skip checking
|
||||
* if the nsILoadURIDelegate will handle the redirect.
|
||||
*/
|
||||
const unsigned long REDIRECT_DELEGATES_CHECKED = 1 << 4;
|
||||
|
||||
/**
|
||||
* Called when a redirect occurs. This may happen due to an HTTP 3xx status
|
||||
* code. The purpose of this method is to notify the sink that a redirect
|
||||
|
|
|
@ -207,6 +207,7 @@ this.AntiTracking = {
|
|||
["privacy.trackingprotection.pbmode.enabled", false],
|
||||
["privacy.trackingprotection.annotate_channels", cookieBehavior != BEHAVIOR_ACCEPT],
|
||||
[win.ContentBlocking.prefIntroCount, win.ContentBlocking.MAX_INTROS],
|
||||
["browser.fastblock.enabled", false], // prevent intermittent failures
|
||||
]});
|
||||
|
||||
if (extraPrefs && Array.isArray(extraPrefs) && extraPrefs.length) {
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
#include "nspr.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
|
||||
#include "nsDocLoader.h"
|
||||
#include "nsCURILoader.h"
|
||||
|
@ -36,8 +34,6 @@
|
|||
#include "nsIDocument.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsILoadURIDelegate.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::LogLevel;
|
||||
|
@ -1425,106 +1421,11 @@ int64_t nsDocLoader::CalculateMaxProgress()
|
|||
return max;
|
||||
}
|
||||
|
||||
class LoadURIDelegateRedirectHandler final : public mozilla::dom::PromiseNativeHandler
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(LoadURIDelegateRedirectHandler)
|
||||
|
||||
LoadURIDelegateRedirectHandler(nsDocLoader* aDocLoader,
|
||||
nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback* aCallback)
|
||||
: mDocLoader(aDocLoader)
|
||||
, mOldChannel(aOldChannel)
|
||||
, mNewChannel(aNewChannel)
|
||||
, mFlags(aFlags)
|
||||
, mCallback(aCallback)
|
||||
{}
|
||||
|
||||
void
|
||||
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
||||
{
|
||||
if (aValue.isBoolean() && aValue.toBoolean()) {
|
||||
// The app handled the redirect, notify the callback
|
||||
mCallback->OnRedirectVerifyCallback(NS_ERROR_ABORT);
|
||||
} else {
|
||||
UnhandledCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
||||
{
|
||||
UnhandledCallback();
|
||||
}
|
||||
|
||||
private:
|
||||
~LoadURIDelegateRedirectHandler()
|
||||
{}
|
||||
|
||||
void UnhandledCallback()
|
||||
{
|
||||
// If the redirect wasn't handled by the nsILoadURIDelegate, let Gecko
|
||||
// handle it.
|
||||
mFlags |= nsIChannelEventSink::REDIRECT_DELEGATES_CHECKED;
|
||||
mDocLoader->AsyncOnChannelRedirect(mOldChannel, mNewChannel, mFlags,
|
||||
mCallback);
|
||||
}
|
||||
|
||||
RefPtr<nsDocLoader> mDocLoader;
|
||||
nsCOMPtr<nsIChannel> mOldChannel;
|
||||
nsCOMPtr<nsIChannel> mNewChannel;
|
||||
uint32_t mFlags;
|
||||
nsCOMPtr<nsIAsyncVerifyRedirectCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(LoadURIDelegateRedirectHandler, mDocLoader,
|
||||
mOldChannel, mNewChannel, mCallback)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadURIDelegateRedirectHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadURIDelegateRedirectHandler)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadURIDelegateRedirectHandler)
|
||||
|
||||
NS_IMETHODIMP nsDocLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
|
||||
nsIChannel *aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback *cb)
|
||||
{
|
||||
if ((aFlags &
|
||||
(nsIChannelEventSink::REDIRECT_TEMPORARY |
|
||||
nsIChannelEventSink::REDIRECT_PERMANENT)) &&
|
||||
!(aFlags & nsIChannelEventSink::REDIRECT_DELEGATES_CHECKED)) {
|
||||
nsCOMPtr<nsIDocShell> docShell =
|
||||
do_QueryInterface(static_cast<nsIRequestObserver*>(this));
|
||||
|
||||
nsCOMPtr<nsILoadURIDelegate> delegate;
|
||||
docShell->GetLoadURIDelegate(getter_AddRefs(delegate));
|
||||
|
||||
nsCOMPtr<nsIURI> newURI;
|
||||
aNewChannel->GetURI(getter_AddRefs(newURI));
|
||||
|
||||
if (newURI && delegate) {
|
||||
RefPtr<mozilla::dom::Promise> promise;
|
||||
const int where = nsIBrowserDOMWindow::OPEN_CURRENTWINDOW;
|
||||
nsresult rv = delegate->LoadURI(newURI, where, /* flags */ 0,
|
||||
/* triggering principal */ nullptr,
|
||||
getter_AddRefs(promise));
|
||||
if (NS_SUCCEEDED(rv) && promise) {
|
||||
RefPtr<LoadURIDelegateRedirectHandler> handler =
|
||||
new LoadURIDelegateRedirectHandler(this, aOldChannel, aNewChannel,
|
||||
aFlags, cb);
|
||||
|
||||
promise->AppendNativeHandler(handler);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aOldChannel)
|
||||
{
|
||||
nsLoadFlags loadFlags = 0;
|
||||
|
|
|
@ -789,7 +789,8 @@ nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext,
|
|||
imgIContainer::FRAME_CURRENT,
|
||||
SamplingFilter::GOOD, /* no SVGImageContext */ Nothing(),
|
||||
imgIContainer::FLAG_SYNC_DECODE, 1.0);
|
||||
if (res == ImgDrawResult::BAD_IMAGE || res == ImgDrawResult::BAD_ARGS) {
|
||||
if (res == ImgDrawResult::BAD_IMAGE || res == ImgDrawResult::BAD_ARGS ||
|
||||
res == ImgDrawResult::NOT_SUPPORTED) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
*aSurface = dt->Snapshot();
|
||||
|
|
|
@ -26,11 +26,10 @@ interface nsILoadURIDelegate : nsISupports
|
|||
* @param aWhere See possible values described in nsIBrowserDOMWindow.
|
||||
* @param aFlags Flags which control the behavior of the load.
|
||||
* @param aTriggeringPrincipal The principal that triggered the load of aURI.
|
||||
* @return A promise which can resolve to a boolean indicating whether or
|
||||
* not the app handled the load. Rejection should be treated the same
|
||||
* as a false resolution.
|
||||
*
|
||||
* Returns whether the load has been successfully handled.
|
||||
*/
|
||||
Promise
|
||||
boolean
|
||||
loadURI(in nsIURI aURI, in short aWhere, in long aFlags,
|
||||
in nsIPrincipal aTriggeringPrincipal);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче