-
Notifications
You must be signed in to change notification settings - Fork 2
Background App
あおいたん edited this page Dec 19, 2013
·
16 revisions
- ${B2G}/gaia/apps/system/js/background_service.js
- window.addEventListener('mozbrowseropenwindow', function bsm_winopen(evt) {
- window.addEventListener('mozbrowserclose', function bsm_winclose(evt) {
- window.addEventListener('mozbrowsererror', function bsm_winclose(evt) {
- window.addEventListener('applicationinstall', function bsm_oninstall(evt) {
- window.addEventListener('applicationuninstall', function bsm_oninstall(evt) {
applicationinstallのリスナ内で
var app = evt.detail.application;なappで
var url = origin + app.manifest.background_page;
open(manifestURL, AUTO_OPEN_BG_PAGE_NAME, url); な感じの処理をしている。
->app.manifest.background_pageは何者か。
- ${B2G}/gecko/xpcom/components/ManifestParser.cpp
- ${B2G}/gaia/apps/system/js/background_service.js
- var hasBackgroundPermission = function bsm_checkPermssion(app) {
で
var mozPerms = navigator.mozPermissionSettings;パーミッションを取ってきて
var value = mozPerms.get('backgroundservice', app.manifestURL,
app.origin, false);
return (value === 'allow');してる。
- https://developer.mozilla.org/id/docs/Apps/Manifest
- https://developer.mozilla.org/en-US/Apps/Developing/App_permissions
を見る感じbackgroundserviceのパーミッションはなんかある。でもCertified。
やってる人いる。 多分FxOS1.2ぐらいまでは動く。
前述のbackground_service.jsもBackgroundServiceManagerもさっぱり消えた。
このあたりのCommitとBugzillaっぽい。
消えてなかった。
- https://github.com/mozilla-b2g/gaia/commit/7f27d41a9202f9390c5e09475fe03fc7f3e77b83
- https://bugzilla.mozilla.org/show_bug.cgi?id=866174
- framesにbackgroundserviceを持つアプリを集めている
- framesに入るのはiframe
- 以下のようなオブジェクトが入ってるっぽい
frame: {
'mozbrowser': 'mozbrowser',
'mozapp': manifestURL, // アプリのマニフェストのURL
'name': name, // アプリ名
'remote': true,
'src': url, // バックグラウンド動作するHTMLのURL
'className': 'backgroundWindow',
'dataset': {
'frameType': 'background',
'frameName': name
}
}- 更にsystemアプリのbodyに入ってる
document.body.appendChild(frame); window.addEventListener('mozbrowseropenwindow', function bsm_winopen(evt) {
window.addEventListener('mozbrowserclose', function bsm_winclose(evt) {
window.addEventListener('mozbrowsererror', function bsm_winclose(evt) {
window.addEventListener('applicationinstall', function bsm_oninstall(evt) {
window.addEventListener('applicationuninstall', function bsm_oninstall(evt) {- こんなかんじでaddEventListenerしてる
- グローバルオブジェクトのwindowにぶら下がっているaddEventListenerなので
- cpp:gecko/dom/base/nsGlobalWindow.cppあたりにいるはず
- グローバルオブジェクトのwindowにぶら下がっているaddEventListenerなので
NS_IMETHODIMP
nsGlobalWindow::AddEventListener(const nsAString& aType,
nsIDOMEventListener *aListener,
bool aUseCapture, bool aWantsUntrusted,
uint8_t aOptionalArgc)
{
NS_ASSERTION(!aWantsUntrusted || aOptionalArgc > 1,
"Won't check if this is chrome, you want to set "
"aWantsUntrusted to false or make the aWantsUntrusted "
"explicit by making optional_argc non-zero.");
if (IsOuterWindow() && mInnerWindow &&
!nsContentUtils::CanCallerAccess(mInnerWindow)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
if (!aWantsUntrusted &&
(aOptionalArgc < 2 && !nsContentUtils::IsChromeDoc(mDoc))) {
aWantsUntrusted = true;
}
nsEventListenerManager* manager = GetListenerManager(true);
NS_ENSURE_STATE(manager);
manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted);
return NS_OK;
}- こんなのがいた
- 要するにイベントリスナマネージャとやらにAddEventListenerしている
void
nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
uint32_t aType,
nsIAtom* aTypeAtom,
int32_t aFlags,
bool aHandler)
{
NS_ABORT_IF_FALSE(aType && aTypeAtom, "Missing type");
if (!aListener || mClearingListeners) {
return;
}
nsRefPtr<nsIDOMEventListener> kungFuDeathGrip = aListener;
nsListenerStruct* ls;
uint32_t count = mListeners.Length();
for (uint32_t i = 0; i < count; i++) {
ls = &mListeners.ElementAt(i);
if (ls->mListener == aListener &&
ls->mListenerIsHandler == aHandler &&
ls->mFlags == aFlags &&
EVENT_TYPE_EQUALS(ls, aType, aTypeAtom)) {
return;
}
}
mNoListenerForEvent = NS_EVENT_TYPE_NULL;
mNoListenerForEventAtom = nullptr;
ls = mListeners.AppendElement();
ls->mListener = aListener;
ls->mEventType = aType;
ls->mTypeAtom = aTypeAtom;
ls->mFlags = aFlags;
ls->mListenerIsHandler = aHandler;
ls->mHandlerIsString = false;
// Detect the type of event listener.
nsCOMPtr<nsIXPConnectWrappedJS> wjs;
if (aFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
ls->mListenerType = eJSEventListener;
} else if ((wjs = do_QueryInterface(aListener))) {
ls->mListenerType = eWrappedJSListener;
} else {
ls->mListenerType = eNativeListener;
}
if (aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) {
mMayHaveSystemGroupListeners = true;
}
if (aFlags & NS_EVENT_FLAG_CAPTURE) {
mMayHaveCapturingListeners = true;
}
if (aType == NS_AFTERPAINT) {
mMayHavePaintEventListener = true;
nsPIDOMWindow* window = GetInnerWindowForTarget();
if (window) {
window->SetHasPaintEventListeners();
}
#ifdef MOZ_MEDIA
} else if (aType == NS_MOZAUDIOAVAILABLE) {
mMayHaveAudioAvailableEventListener = true;
nsPIDOMWindow* window = GetInnerWindowForTarget();
if (window) {
window->SetHasAudioAvailableEventListeners();
}
#endif // MOZ_MEDIA
} else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) {
// For mutation listeners, we need to update the global bit on the DOM window.
// Otherwise we won't actually fire the mutation event.
mMayHaveMutationListeners = true;
// Go from our target to the nearest enclosing DOM window.
nsPIDOMWindow* window = GetInnerWindowForTarget();
if (window) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
if (doc) {
doc->WarnOnceAbout(nsIDocument::eMutationEvent);
}
// If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
// mutations. nsContentUtils::HasMutationListeners relies on this.
window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ?
kAllMutationBits :
MutationBitForEventType(aType));
}
} else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
EnableDevice(NS_DEVICE_ORIENTATION);
} else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
EnableDevice(NS_DEVICE_PROXIMITY);
} else if (aTypeAtom == nsGkAtoms::ondevicelight) {
EnableDevice(NS_DEVICE_LIGHT);
} else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
EnableDevice(NS_DEVICE_MOTION);
#ifdef MOZ_B2G
} else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
if (window) {
window->EnableTimeChangeNotifications();
}
} else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
if (window) {
window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT);
}
} else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) {
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
if (window) {
window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT);
}
#endif // MOZ_B2G
} else if (aTypeAtom == nsGkAtoms::ontouchstart ||
aTypeAtom == nsGkAtoms::ontouchend ||
aTypeAtom == nsGkAtoms::ontouchmove ||
aTypeAtom == nsGkAtoms::ontouchenter ||
aTypeAtom == nsGkAtoms::ontouchleave ||
aTypeAtom == nsGkAtoms::ontouchcancel) {
mMayHaveTouchEventListener = true;
nsPIDOMWindow* window = GetInnerWindowForTarget();
// we don't want touchevent listeners added by scrollbars to flip this flag
// so we ignore listeners created with system event flag
if (window && !(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT))
window->SetHasTouchEventListeners();
} else if (aTypeAtom == nsGkAtoms::onmouseenter ||
aTypeAtom == nsGkAtoms::onmouseleave) {
mMayHaveMouseEnterLeaveEventListener = true;
nsPIDOMWindow* window = GetInnerWindowForTarget();
if (window) {
#ifdef DEBUG
nsCOMPtr<nsIDocument> d = do_QueryInterface(window->GetExtantDocument());
NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d),
"Please do not use mouseenter/leave events in chrome. "
"They are slower than mouseover/out!");
#endif
window->SetHasMouseEnterLeaveEventListeners();
}
}
}- すごく長いけど要するにリスナの配列に渡されたリスナを格納してるだけ
- 長いのはフラグとかタイプによって後処理を変えたりするための値の加工と保持とか別のイベントリスナへの登録とか
- gecko/dom/browser-element/BrowserElementParent.cppにDispatchOpenWindowEventがいる
bool
DispatchOpenWindowEvent(Element* aOpenerFrameElement,
Element* aPopupFrameElement,
const nsAString& aURL,
const nsAString& aName,
const nsAString& aFeatures)
{
// Dispatch a CustomEvent at aOpenerFrameElement with a detail object
// (nsIOpenWindowEventDetail) containing aPopupFrameElement, aURL, aName, and
// aFeatures.
// Create the event's detail object.
nsRefPtr<nsOpenWindowEventDetail> detail =
new nsOpenWindowEventDetail(aURL, aName, aFeatures,
aPopupFrameElement->AsDOMNode());
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
NS_LITERAL_STRING("mozbrowseropenwindow"),
detail);
// If the iframe is not in some document's DOM at this point, the embedder
// has "blocked" the popup.
return (dispatchSucceeded && aPopupFrameElement->IsInDoc());
}- キモの部分は
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
NS_LITERAL_STRING("mozbrowseropenwindow"),
detail);- そのDispatchCustomDOMEventはこんな感じ
bool
DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
nsISupports *aDetailValue)
{
NS_ENSURE_TRUE(aFrameElement, false);
nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell();
nsRefPtr<nsPresContext> presContext;
if (shell) {
presContext = shell->GetPresContext();
}
nsCOMPtr<nsIDOMEvent> domEvent;
nsEventDispatcher::CreateEvent(presContext, nullptr,
NS_LITERAL_STRING("customevent"),
getter_AddRefs(domEvent));
NS_ENSURE_TRUE(domEvent, false);
nsCOMPtr<nsIWritableVariant> detailVariant = new nsVariant();
nsresult rv = detailVariant->SetAsISupports(aDetailValue);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
NS_ENSURE_TRUE(customEvent, false);
customEvent->InitCustomEvent(aEventName,
/* bubbles = */ true,
/* cancelable = */ false,
detailVariant);
customEvent->SetTrusted(true);
// Dispatch the event.
nsEventStatus status = nsEventStatus_eIgnore;
rv = nsEventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
domEvent, presContext, &status);
return NS_SUCCEEDED(rv);
}- カスタムイベントオブジェクトを作ってDispatchDOMEventしている
nsresult
nsGlobalWindow::DispatchDOMEvent(nsEvent* aEvent,
nsIDOMEvent* aDOMEvent,
nsPresContext* aPresContext,
nsEventStatus* aEventStatus)
{
return
nsEventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this),
aEvent, aDOMEvent, aPresContext,
aEventStatus);
}/* static */ nsresult
nsEventDispatcher::DispatchDOMEvent(nsISupports* aTarget,
nsEvent* aEvent,
nsIDOMEvent* aDOMEvent,
nsPresContext* aPresContext,
nsEventStatus* aEventStatus)
{
if (aDOMEvent) {
nsEvent* innerEvent = aDOMEvent->GetInternalNSEvent();
NS_ENSURE_TRUE(innerEvent, NS_ERROR_ILLEGAL_VALUE);
bool dontResetTrusted = false;
if (innerEvent->flags & NS_EVENT_DISPATCHED) {
innerEvent->target = nullptr;
innerEvent->originalTarget = nullptr;
} else {
aDOMEvent->GetIsTrusted(&dontResetTrusted);
}
if (!dontResetTrusted) {
//Check security state to determine if dispatcher is trusted
aDOMEvent->SetTrusted(nsContentUtils::IsCallerTrustedForWrite());
}
return nsEventDispatcher::Dispatch(aTarget, aPresContext, innerEvent,
aDOMEvent, aEventStatus);
} else if (aEvent) {
return nsEventDispatcher::Dispatch(aTarget, aPresContext, aEvent,
aDOMEvent, aEventStatus);
}
return NS_ERROR_ILLEGAL_VALUE;
}/* static */ nsresult
nsEventDispatcher::Dispatch(nsISupports* aTarget,
nsPresContext* aPresContext,
nsEvent* aEvent,
nsIDOMEvent* aDOMEvent,
nsEventStatus* aEventStatus,
nsDispatchingCallback* aCallback,
nsCOMArray<nsIDOMEventTarget>* aTargets)
{
SAMPLE_LABEL("nsEventDispatcher", "Dispatch");
NS_ASSERTION(aEvent, "Trying to dispatch without nsEvent!");
NS_ENSURE_TRUE(!NS_IS_EVENT_IN_DISPATCH(aEvent),
NS_ERROR_ILLEGAL_VALUE);
NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
// If we're dispatching an already created DOMEvent object, make
// sure it is initialized!
// If aTargets is non-null, the event isn't going to be dispatched.
NS_ENSURE_TRUE(aEvent->message || !aDOMEvent || aTargets,
NS_ERROR_DOM_INVALID_STATE_ERR);
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(aTarget);
bool retargeted = false;
if (aEvent->flags & NS_EVENT_RETARGET_TO_NON_NATIVE_ANONYMOUS) {
nsCOMPtr<nsIContent> content = do_QueryInterface(target);
if (content && content->IsInNativeAnonymousSubtree()) {
nsCOMPtr<nsPIDOMEventTarget> newTarget =
do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
NS_ENSURE_STATE(newTarget);
aEvent->originalTarget = target;
target = newTarget;
retargeted = true;
}
}
if (aEvent->flags & NS_EVENT_FLAG_ONLY_CHROME_DISPATCH) {
nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
if (!node) {
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget);
if (win) {
node = do_QueryInterface(win->GetExtantDocument());
}
}
NS_ENSURE_STATE(node);
nsIDocument* doc = node->OwnerDoc();
if (!nsContentUtils::IsChromeDoc(doc)) {
nsPIDOMWindow* win = doc ? doc->GetInnerWindow() : nullptr;
// If we can't dispatch the event to chrome, do nothing.
nsIDOMEventTarget* piTarget = win ? win->GetParentTarget() : nullptr;
NS_ENSURE_TRUE(piTarget, NS_OK);
// Set the target to be the original dispatch target,
aEvent->target = target;
// but use chrome event handler or TabChildGlobal for event target chain.
target = piTarget;
}
}
#ifdef DEBUG
if (!nsContentUtils::IsSafeToRunScript()) {
nsresult rv = NS_ERROR_FAILURE;
if (target->GetContextForEventHandlers(&rv) ||
NS_FAILED(rv)) {
nsCOMPtr<nsINode> node = do_QueryInterface(target);
if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) {
NS_WARNING("Fix the caller!");
} else {
NS_ERROR("This is unsafe! Fix the caller!");
}
}
}
if (aDOMEvent) {
nsEvent* innerEvent = aDOMEvent->GetInternalNSEvent();
NS_ASSERTION(innerEvent == aEvent,
"The inner event of aDOMEvent is not the same as aEvent!");
}
#endif
nsresult rv = NS_OK;
bool externalDOMEvent = !!(aDOMEvent);
// If we have a PresContext, make sure it doesn't die before
// event dispatching is finished.
nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
ChainItemPool pool;
NS_ENSURE_TRUE(pool.GetPool(), NS_ERROR_OUT_OF_MEMORY);
// Create the event target chain item for the event target.
nsEventTargetChainItem* targetEtci =
nsEventTargetChainItem::Create(pool.GetPool(),
target->GetTargetForEventTargetChain());
NS_ENSURE_TRUE(targetEtci, NS_ERROR_OUT_OF_MEMORY);
if (!targetEtci->IsValid()) {
nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci);
return NS_ERROR_FAILURE;
}
// Make sure that nsIDOMEvent::target and nsIDOMEvent::originalTarget
// point to the last item in the chain.
if (!aEvent->target) {
// Note, CurrentTarget() points always to the object returned by
// GetTargetForEventTargetChain().
aEvent->target = targetEtci->CurrentTarget();
} else {
// XXX But if the target is already set, use that. This is a hack
// for the 'load', 'beforeunload' and 'unload' events,
// which are dispatched to |window| but have document as their target.
//
// Make sure that the event target points to the right object.
aEvent->target = aEvent->target->GetTargetForEventTargetChain();
NS_ENSURE_STATE(aEvent->target);
}
if (retargeted) {
aEvent->originalTarget =
aEvent->originalTarget->GetTargetForEventTargetChain();
NS_ENSURE_STATE(aEvent->originalTarget);
}
else {
aEvent->originalTarget = aEvent->target;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(aEvent->originalTarget);
bool isInAnon = (content && content->IsInAnonymousSubtree());
NS_MARK_EVENT_DISPATCH_STARTED(aEvent);
// Create visitor object and start event dispatching.
// PreHandleEvent for the original target.
nsEventStatus status = aEventStatus ? *aEventStatus : nsEventStatus_eIgnore;
nsEventChainPreVisitor preVisitor(aPresContext, aEvent, aDOMEvent, status,
isInAnon);
targetEtci->PreHandleEvent(preVisitor);
if (preVisitor.mCanHandle) {
// At least the original target can handle the event.
// Setting the retarget to the |target| simplifies retargeting code.
nsCOMPtr<nsIDOMEventTarget> t = aEvent->target;
targetEtci->SetNewTarget(t);
nsEventTargetChainItem* topEtci = targetEtci;
while (preVisitor.mParentTarget) {
nsEventTargetChainItem* parentEtci =
nsEventTargetChainItem::Create(pool.GetPool(), preVisitor.mParentTarget,
topEtci);
if (!parentEtci) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
if (!parentEtci->IsValid()) {
rv = NS_ERROR_FAILURE;
break;
}
// Item needs event retargetting.
if (preVisitor.mEventTargetAtParent) {
// Need to set the target of the event
// so that also the next retargeting works.
preVisitor.mEvent->target = preVisitor.mEventTargetAtParent;
parentEtci->SetNewTarget(preVisitor.mEventTargetAtParent);
}
parentEtci->PreHandleEvent(preVisitor);
if (preVisitor.mCanHandle) {
topEtci = parentEtci;
} else {
nsEventTargetChainItem::Destroy(pool.GetPool(), parentEtci);
parentEtci = nullptr;
break;
}
}
if (NS_SUCCEEDED(rv)) {
if (aTargets) {
aTargets->Clear();
nsEventTargetChainItem* item = targetEtci;
while(item) {
aTargets->AppendObject(item->CurrentTarget()->GetTargetForDOMEvent());
item = item->mParent;
}
} else {
// Event target chain is created. Handle the chain.
nsEventChainPostVisitor postVisitor(preVisitor);
nsCxPusher pusher;
rv = topEtci->HandleEventTargetChain(postVisitor,
NS_EVENT_FLAG_BUBBLE |
NS_EVENT_FLAG_CAPTURE,
aCallback,
false,
&pusher);
preVisitor.mEventStatus = postVisitor.mEventStatus;
// If the DOM event was created during event flow.
if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
preVisitor.mDOMEvent = postVisitor.mDOMEvent;
}
}
}
}
nsEventTargetChainItem::Destroy(pool.GetPool(), targetEtci);
targetEtci = nullptr;
NS_MARK_EVENT_DISPATCH_DONE(aEvent);
if (!externalDOMEvent && preVisitor.mDOMEvent) {
// An nsDOMEvent was created while dispatching the event.
// Duplicate private data if someone holds a pointer to it.
nsrefcnt rc = 0;
NS_RELEASE2(preVisitor.mDOMEvent, rc);
if (preVisitor.mDOMEvent) {
preVisitor.mDOMEvent->DuplicatePrivateData();
}
}
if (aEventStatus) {
*aEventStatus = preVisitor.mEventStatus;
}
return rv;
}- 時間切れで追いきれてませんがざっくり見た感じ
-
カスタムイベントのオブジェクトからDispatchCustomEventされると
-
nsGlobalWindowを経由してnsEventDispatcherのDispatchEventまで流れてくる
-
nsEventDispatcher::DispatchEventでは
-
イベントのオブジェクトを作る
-
送信先のリストを作る
- 対象のDOM要素から親へ親へたどってる?
- 教えてエライ人!
- 対象のDOM要素から親へ親へたどってる?
-
以下のコードでpostVisitorに詰めた配信対象のDOM要素に対してイベントオブジェクトを投げつけていくはず
rv = topEtci->HandleEventTargetChain(postVisitor, NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE, aCallback, false, &pusher); ```
* 教えてエライ人! -
-
mozbrowseropenwindow以外のイベントが見つからないけどカスタムイベントじゃないから?
- 教えてエライ人!
-