nsresult PackPDU(const BluetoothAvrcpElementAttribute& aIn, BluetoothDaemonPDU& aPDU) { nsresult rv = PackPDU(PackConversion<uint32_t, uint8_t>(aIn.mId), aPDU); if (NS_FAILED(rv)) { return rv; } const NS_ConvertUTF16toUTF8 cstr(aIn.mValue); if (NS_WARN_IF(cstr.Length() == PR_UINT32_MAX)) { return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */ } PRUint32 clen = cstr.Length() + 1; /* include \0 character */ rv = PackPDU(PackConversion<PRUint32, uint8_t>(clen), aPDU); if (NS_FAILED(rv)) { return rv; } return PackPDU( PackArray<uint8_t>(reinterpret_cast<const uint8_t*>(cstr.get()), clen), aPDU); }
nsresult Convert(const BluetoothAvrcpElementAttribute& aIn, btrc_element_attr_val_t& aOut) { const NS_ConvertUTF16toUTF8 value(aIn.mValue); size_t len = std::min<size_t>(strlen(value.get()), sizeof(aOut.text) - 1); memcpy(aOut.text, value.get(), len); aOut.text[len] = '\0'; aOut.attr_id = aIn.mId; return NS_OK; }
bool nsAppShell::ProcessNextNativeEvent(bool mayWait) { EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait); nsAutoPtr<AndroidGeckoEvent> curEvent; { MutexAutoLock lock(mCondLock); curEvent = PopNextEvent(); if (!curEvent && mayWait) { // hmm, should we really hardcode this 10s? #if defined(DEBUG_ANDROID_EVENTS) PRTime t0, t1; EVLOG("nsAppShell: waiting on mQueueCond"); t0 = PR_Now(); mQueueCond.Wait(PR_MillisecondsToInterval(10000)); t1 = PR_Now(); EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000); #else mQueueCond.Wait(); #endif curEvent = PopNextEvent(); } } if (!curEvent) return false; EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type()); switch (curEvent->Type()) { case AndroidGeckoEvent::NATIVE_POKE: NativeEventCallback(); break; case AndroidGeckoEvent::SENSOR_EVENT: { InfallibleTArray<float> values; mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); switch (type) { case hal::SENSOR_ORIENTATION: case hal::SENSOR_LINEAR_ACCELERATION: case hal::SENSOR_ACCELERATION: case hal::SENSOR_GYROSCOPE: values.AppendElement(curEvent->X()); values.AppendElement(curEvent->Y()); values.AppendElement(curEvent->Z()); break; case hal::SENSOR_PROXIMITY: values.AppendElement(curEvent->X()); break; default: __android_log_print(ANDROID_LOG_ERROR, "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d", type); } const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState(); hal::SensorData sdata(type, PR_Now(), values, accuracy); hal::NotifySensorChange(sdata); } break; case AndroidGeckoEvent::LOCATION_EVENT: { if (!gLocationCallback) break; nsGeoPosition* p = curEvent->GeoPosition(); if (p) gLocationCallback->Update(curEvent->GeoPosition()); else NS_WARNING("Received location event without geoposition!"); break; } case AndroidGeckoEvent::ACTIVITY_STOPPING: { if (curEvent->Flags() > 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); obsServ->NotifyObservers(nsnull, "memory-pressure", minimize.get()); obsServ->NotifyObservers(nsnull, "application-background", nsnull); break; } case AndroidGeckoEvent::ACTIVITY_SHUTDOWN: { nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(context, "shutdown-persist"); obsServ->NotifyObservers(nsnull, "quit-application-granted", nsnull); obsServ->NotifyObservers(nsnull, "quit-application-forced", nsnull); obsServ->NotifyObservers(nsnull, "profile-change-net-teardown", context.get()); obsServ->NotifyObservers(nsnull, "profile-change-teardown", context.get()); obsServ->NotifyObservers(nsnull, "profile-before-change", context.get()); nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1"); if (appSvc) appSvc->Quit(nsIAppStartup::eForceQuit); break; } case AndroidGeckoEvent::ACTIVITY_PAUSING: { if (curEvent->Flags() == 0) { // We aren't transferring to one of our own activities, so set // background status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-background", nsnull); // If we are OOM killed with the disk cache enabled, the entire // cache will be cleared (bug 105843), so shut down the cache here // and re-init on resume if (nsCacheService::GlobalInstance()) nsCacheService::GlobalInstance()->Shutdown(); } // We really want to send a notification like profile-before-change, // but profile-before-change ends up shutting some things down instead // of flushing data nsIPrefService* prefs = Preferences::GetService(); if (prefs) { // reset the crash loop state nsCOMPtr<nsIPrefBranch> prefBranch; prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch)); if (prefBranch) prefBranch->SetIntPref("recent_crashes", 0); prefs->SavePrefFile(nsnull); } break; } case AndroidGeckoEvent::ACTIVITY_START: { if (curEvent->Flags() > 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); break; } case AndroidGeckoEvent::SCREENSHOT: { if (!mBrowserApp) break; AndroidBridge* bridge = AndroidBridge::Bridge(); if (!bridge) break; nsCOMPtr<nsIDOMWindow> domWindow; nsCOMPtr<nsIBrowserTab> tab; float scale; mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab)); if (!tab) break; tab->GetWindow(getter_AddRefs(domWindow)); if (!domWindow) break; if (NS_FAILED(tab->GetScale(&scale))) break; nsTArray<nsIntPoint> points = curEvent->Points(); NS_ASSERTION(points.Length() == 2, "Screenshot event does not have enough coordinates"); bridge->TakeScreenshot(domWindow, 0, 0, points[0].x, points[0].y, points[1].x, points[1].y, curEvent->MetaState(), scale); break; } case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { if (curEvent->Characters().Length() == 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); const NS_ConvertUTF16toUTF8 topic(curEvent->Characters()); const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra()); obsServ->NotifyObservers(nsnull, topic.get(), data.get()); break; } case AndroidGeckoEvent::LOAD_URI: { nsCOMPtr<nsICommandLineRunner> cmdline (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); if (!cmdline) break; if (curEvent->Characters().Length() == 0) break; char *uri = ToNewUTF8String(curEvent->Characters()); if (!uri) break; char *flag = ToNewUTF8String(curEvent->CharactersExtra()); const char *argv[4] = { "dummyappname", "-url", uri, flag ? flag : "" }; nsresult rv = cmdline->Init(4, const_cast<char **>(argv), nsnull, nsICommandLine::STATE_REMOTE_AUTO); if (NS_SUCCEEDED(rv)) cmdline->Run(); nsMemory::Free(uri); if (flag) nsMemory::Free(flag); break; } case AndroidGeckoEvent::SIZE_CHANGED: { // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event if (curEvent != gLastSizeChange) { gLastSizeChange = new AndroidGeckoEvent(curEvent); } nsWindow::OnGlobalAndroidEvent(curEvent); break; } case AndroidGeckoEvent::VISITED: { #ifdef MOZ_ANDROID_HISTORY nsAndroidHistory::NotifyURIVisited(nsString(curEvent->Characters())); #endif break; } case AndroidGeckoEvent::NETWORK_CHANGED: { hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->Bandwidth(), curEvent->CanBeMetered())); break; } case AndroidGeckoEvent::ACTIVITY_RESUMING: { if (curEvent->Flags() == 0) { // If we are OOM killed with the disk cache enabled, the entire // cache will be cleared (bug 105843), so shut down cache on pause // and re-init here if (nsCacheService::GlobalInstance()) nsCacheService::GlobalInstance()->Init(); // We didn't return from one of our own activities, so restore // to foreground status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); } break; } case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: { hal::NotifyScreenOrientationChange(static_cast<dom::ScreenOrientation>(curEvent->ScreenOrientation())); break; } default: nsWindow::OnGlobalAndroidEvent(curEvent); } EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type()); return true; }
void nsAppShell::LegacyGeckoEvent::Run() { const mozilla::UniquePtr<AndroidGeckoEvent>& curEvent = ae; EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type()); switch (curEvent->Type()) { case AndroidGeckoEvent::NATIVE_POKE: nsAppShell::gAppShell->NativeEventCallback(); break; case AndroidGeckoEvent::SENSOR_EVENT: { nsAutoTArray<float, 4> values; mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); switch (type) { // Bug 938035, transfer HAL data for orientation sensor to meet w3c // spec, ex: HAL report alpha=90 means East but alpha=90 means West // in w3c spec case hal::SENSOR_ORIENTATION: values.AppendElement(360 -curEvent->X()); values.AppendElement(-curEvent->Y()); values.AppendElement(-curEvent->Z()); break; case hal::SENSOR_LINEAR_ACCELERATION: case hal::SENSOR_ACCELERATION: case hal::SENSOR_GYROSCOPE: case hal::SENSOR_PROXIMITY: values.AppendElement(curEvent->X()); values.AppendElement(curEvent->Y()); values.AppendElement(curEvent->Z()); break; case hal::SENSOR_LIGHT: values.AppendElement(curEvent->X()); break; case hal::SENSOR_ROTATION_VECTOR: case hal::SENSOR_GAME_ROTATION_VECTOR: values.AppendElement(curEvent->X()); values.AppendElement(curEvent->Y()); values.AppendElement(curEvent->Z()); values.AppendElement(curEvent->W()); break; default: __android_log_print(ANDROID_LOG_ERROR, "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d", type); } const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState(); hal::SensorData sdata(type, PR_Now(), values, accuracy); hal::NotifySensorChange(sdata); } break; case AndroidGeckoEvent::PROCESS_OBJECT: { switch (curEvent->Action()) { case AndroidGeckoEvent::ACTION_OBJECT_LAYER_CLIENT: AndroidBridge::Bridge()->SetLayerClient( widget::GeckoLayerClient::Ref::From(curEvent->Object().wrappedObject())); break; } break; } case AndroidGeckoEvent::LOCATION_EVENT: { if (!gLocationCallback) break; nsGeoPosition* p = curEvent->GeoPosition(); if (p) gLocationCallback->Update(curEvent->GeoPosition()); else NS_WARNING("Received location event without geoposition!"); break; } case AndroidGeckoEvent::APP_BACKGROUNDING: { nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nullptr, "application-background", nullptr); NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get()); // If we are OOM killed with the disk cache enabled, the entire // cache will be cleared (bug 105843), so shut down the cache here // and re-init on foregrounding if (nsCacheService::GlobalInstance()) { nsCacheService::GlobalInstance()->Shutdown(); } // We really want to send a notification like profile-before-change, // but profile-before-change ends up shutting some things down instead // of flushing data nsIPrefService* prefs = Preferences::GetService(); if (prefs) { // reset the crash loop state nsCOMPtr<nsIPrefBranch> prefBranch; prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch)); if (prefBranch) prefBranch->SetIntPref("recent_crashes", 0); prefs->SavePrefFile(nullptr); } break; } case AndroidGeckoEvent::APP_FOREGROUNDING: { // If we are OOM killed with the disk cache enabled, the entire // cache will be cleared (bug 105843), so shut down cache on backgrounding // and re-init here if (nsCacheService::GlobalInstance()) { nsCacheService::GlobalInstance()->Init(); } // We didn't return from one of our own activities, so restore // to foreground status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nullptr, "application-foreground", nullptr); break; } case AndroidGeckoEvent::THUMBNAIL: { if (!nsAppShell::gAppShell->mBrowserApp) break; int32_t tabId = curEvent->MetaState(); const nsTArray<nsIntPoint>& points = curEvent->Points(); RefCountedJavaObject* buffer = curEvent->ByteBuffer(); nsRefPtr<ThumbnailRunnable> sr = new ThumbnailRunnable(nsAppShell::gAppShell->mBrowserApp, tabId, points, buffer); MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableMethod(sr.get(), &ThumbnailRunnable::Run)); break; } case AndroidGeckoEvent::ZOOMEDVIEW: { if (!nsAppShell::gAppShell->mBrowserApp) break; int32_t tabId = curEvent->MetaState(); const nsTArray<nsIntPoint>& points = curEvent->Points(); float scaleFactor = (float) curEvent->X(); nsRefPtr<RefCountedJavaObject> javaBuffer = curEvent->ByteBuffer(); const auto& mBuffer = jni::Object::Ref::From(javaBuffer->GetObject()); nsCOMPtr<nsIDOMWindow> domWindow; nsCOMPtr<nsIBrowserTab> tab; nsAppShell::gAppShell->mBrowserApp->GetBrowserTab(tabId, getter_AddRefs(tab)); if (!tab) { NS_ERROR("Can't find tab!"); break; } tab->GetWindow(getter_AddRefs(domWindow)); if (!domWindow) { NS_ERROR("Can't find dom window!"); break; } NS_ASSERTION(points.Length() == 2, "ZoomedView event does not have enough coordinates"); nsIntRect r(points[0].x, points[0].y, points[1].x, points[1].y); AndroidBridge::Bridge()->CaptureZoomedView(domWindow, r, mBuffer, scaleFactor); break; } case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { if (curEvent->Characters().Length() == 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); const NS_ConvertUTF16toUTF8 topic(curEvent->Characters()); const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra()); obsServ->NotifyObservers(nullptr, topic.get(), data.get()); break; } case AndroidGeckoEvent::TELEMETRY_UI_SESSION_STOP: { if (curEvent->Characters().Length() == 0) break; nsCOMPtr<nsIUITelemetryObserver> obs; nsAppShell::gAppShell->mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); if (!obs) break; obs->StopSession( nsString(curEvent->Characters()).get(), nsString(curEvent->CharactersExtra()).get(), curEvent->Time() ); break; } case AndroidGeckoEvent::TELEMETRY_UI_SESSION_START: { if (curEvent->Characters().Length() == 0) break; nsCOMPtr<nsIUITelemetryObserver> obs; nsAppShell::gAppShell->mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); if (!obs) break; obs->StartSession( nsString(curEvent->Characters()).get(), curEvent->Time() ); break; } case AndroidGeckoEvent::TELEMETRY_UI_EVENT: { if (curEvent->Data().Length() == 0) break; nsCOMPtr<nsIUITelemetryObserver> obs; nsAppShell::gAppShell->mBrowserApp->GetUITelemetryObserver(getter_AddRefs(obs)); if (!obs) break; obs->AddEvent( nsString(curEvent->Data()).get(), nsString(curEvent->Characters()).get(), curEvent->Time(), nsString(curEvent->CharactersExtra()).get() ); break; } case AndroidGeckoEvent::LOAD_URI: { nsCOMPtr<nsICommandLineRunner> cmdline (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); if (!cmdline) break; if (curEvent->Characters().Length() == 0) break; char *uri = ToNewUTF8String(curEvent->Characters()); if (!uri) break; char *flag = ToNewUTF8String(curEvent->CharactersExtra()); const char *argv[4] = { "dummyappname", "-url", uri, flag ? flag : "" }; nsresult rv = cmdline->Init(4, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO); if (NS_SUCCEEDED(rv)) cmdline->Run(); free(uri); if (flag) free(flag); break; } case AndroidGeckoEvent::SIZE_CHANGED: { // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event if (curEvent.get() != gLastSizeChange) { gLastSizeChange = AndroidGeckoEvent::CopyResizeEvent(curEvent.get()); } nsWindow::OnGlobalAndroidEvent(curEvent.get()); break; } case AndroidGeckoEvent::VISITED: { #ifdef MOZ_ANDROID_HISTORY nsCOMPtr<IHistory> history = services::GetHistoryService(); nsCOMPtr<nsIURI> visitedURI; if (history && NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI), nsString(curEvent->Characters())))) { history->NotifyVisited(visitedURI); } #endif break; } case AndroidGeckoEvent::NETWORK_CHANGED: { hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->ConnectionType(), curEvent->IsWifi(), curEvent->DHCPGateway())); break; } case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: { nsresult rv; nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv); if (NS_FAILED(rv)) { NS_ERROR("Can't find nsIScreenManager!"); break; } nsIntRect rect; int32_t colorDepth, pixelDepth; int16_t angle; dom::ScreenOrientationInternal orientation; nsCOMPtr<nsIScreen> screen; screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height); screen->GetColorDepth(&colorDepth); screen->GetPixelDepth(&pixelDepth); orientation = static_cast<dom::ScreenOrientationInternal>(curEvent->ScreenOrientation()); angle = curEvent->ScreenAngle(); hal::NotifyScreenConfigurationChange( hal::ScreenConfiguration(rect, orientation, angle, colorDepth, pixelDepth)); break; } case AndroidGeckoEvent::CALL_OBSERVER: { nsCOMPtr<nsIObserver> observer; nsAppShell::gAppShell->mObserversHash.Get(curEvent->Characters(), getter_AddRefs(observer)); if (observer) { observer->Observe(nullptr, NS_ConvertUTF16toUTF8(curEvent->CharactersExtra()).get(), nsString(curEvent->Data()).get()); } else { ALOG("Call_Observer event: Observer was not found!"); } break; } case AndroidGeckoEvent::REMOVE_OBSERVER: nsAppShell::gAppShell->mObserversHash.Remove(curEvent->Characters()); break; case AndroidGeckoEvent::ADD_OBSERVER: nsAppShell::gAppShell->AddObserver(curEvent->Characters(), curEvent->Observer()); break; case AndroidGeckoEvent::LOW_MEMORY: // TODO hook in memory-reduction stuff for different levels here if (curEvent->MetaState() >= AndroidGeckoEvent::MEMORY_PRESSURE_MEDIUM) { nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { os->NotifyObservers(nullptr, "memory-pressure", MOZ_UTF16("low-memory")); } } break; case AndroidGeckoEvent::NETWORK_LINK_CHANGE: { nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { os->NotifyObservers(nullptr, NS_NETWORK_LINK_TOPIC, nsString(curEvent->Characters()).get()); } break; } case AndroidGeckoEvent::TELEMETRY_HISTOGRAM_ADD: // If the extras field is not empty then this is a keyed histogram. if (!curEvent->CharactersExtra().IsVoid()) { Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(), NS_ConvertUTF16toUTF8(curEvent->CharactersExtra()), curEvent->Count()); } else { Telemetry::Accumulate(NS_ConvertUTF16toUTF8(curEvent->Characters()).get(), curEvent->Count()); } break; case AndroidGeckoEvent::GAMEPAD_ADDREMOVE: { #ifdef MOZ_GAMEPAD if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_ADDED) { int svc_id = dom::GamepadFunctions::AddGamepad("android", dom::GamepadMappingType::Standard, dom::kStandardGamepadButtons, dom::kStandardGamepadAxes); widget::GeckoAppShell::GamepadAdded(curEvent->ID(), svc_id); } else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_REMOVED) { dom::GamepadFunctions::RemoveGamepad(curEvent->ID()); } #endif break; } case AndroidGeckoEvent::GAMEPAD_DATA: { #ifdef MOZ_GAMEPAD int id = curEvent->ID(); if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_BUTTON) { dom::GamepadFunctions::NewButtonEvent(id, curEvent->GamepadButton(), curEvent->GamepadButtonPressed(), curEvent->GamepadButtonValue()); } else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_AXES) { int valid = curEvent->Flags(); const nsTArray<float>& values = curEvent->GamepadValues(); for (unsigned i = 0; i < values.Length(); i++) { if (valid & (1<<i)) { dom::GamepadFunctions::NewAxisMoveEvent(id, i, values[i]); } } } #endif break; } case AndroidGeckoEvent::NOOP: break; default: nsWindow::OnGlobalAndroidEvent(curEvent.get()); break; } if (curEvent->AckNeeded()) { widget::GeckoAppShell::AcknowledgeEvent(); } EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type()); }
bool nsAppShell::ProcessNextNativeEvent(bool mayWait) { EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait); nsAutoPtr<AndroidGeckoEvent> curEvent; AndroidGeckoEvent *nextEvent; { MutexAutoLock lock(mCondLock); curEvent = PopNextEvent(); if (!curEvent && mayWait) { // hmm, should we really hardcode this 10s? #if defined(DEBUG_ANDROID_EVENTS) PRTime t0, t1; EVLOG("nsAppShell: waiting on mQueueCond"); t0 = PR_Now(); mQueueCond.Wait(PR_MillisecondsToInterval(10000)); t1 = PR_Now(); EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000); #else mQueueCond.Wait(); #endif curEvent = PopNextEvent(); } } if (!curEvent) return false; // Combine subsequent events of the same type nextEvent = PeekNextEvent(); while (nextEvent) { int curType = curEvent->Type(); int nextType = nextEvent->Type(); while (nextType == AndroidGeckoEvent::VIEWPORT && mNumViewports > 1) { // Skip this viewport change, as there's another one later and // processing this one will only cause more unnecessary work PopNextEvent(); delete nextEvent; nextEvent = PeekNextEvent(); nextType = nextEvent->Type(); } while (nextType == AndroidGeckoEvent::DRAW && mLastDrawEvent && mNumDraws > 1) { // skip this draw, since there's a later one already in the queue.. this will let us // deal with sequences that look like: // MOVE DRAW MOVE DRAW MOVE DRAW // and end up with just // MOVE DRAW // when we process all the events. // Combine the next draw event's rect with the last one in the queue const nsIntRect& nextRect = nextEvent->Rect(); const nsIntRect& lastRect = mLastDrawEvent->Rect(); int combinedArea = (lastRect.width * lastRect.height) + (nextRect.width * nextRect.height); nsIntRect combinedRect = lastRect.Union(nextRect); mLastDrawEvent->Init(AndroidGeckoEvent::DRAW, combinedRect); // XXX We may want to consider using regions instead of rectangles. // Print an error if we're upload a lot more than we would // if we handled this as two separate events. int boundsArea = combinedRect.width * combinedRect.height; if (boundsArea > combinedArea * 8) ALOG("nsAppShell::ProcessNextNativeEvent: " "Area of bounds greatly exceeds combined area: %d > %d", boundsArea, combinedArea); // Remove the next draw event PopNextEvent(); delete nextEvent; #if defined(DEBUG_ANDROID_EVENTS) ALOG("# Removing DRAW event (%d outstanding)", mNumDraws); #endif nextEvent = PeekNextEvent(); nextType = nextEvent->Type(); } // If the next type of event isn't the same as the current type, // we don't coalesce. if (nextType != curType) break; // Can only coalesce motion move events, for motion events if (curType != AndroidGeckoEvent::MOTION_EVENT) break; if (!(curEvent->Action() == AndroidMotionEvent::ACTION_MOVE && nextEvent->Action() == AndroidMotionEvent::ACTION_MOVE)) break; #if defined(DEBUG_ANDROID_EVENTS) ALOG("# Removing % 2d event", curType); #endif curEvent = PopNextEvent(); nextEvent = PeekNextEvent(); } EVLOG("nsAppShell: event %p %d [ndraws %d]", (void*)curEvent.get(), curEvent->Type(), mNumDraws); switch (curEvent->Type()) { case AndroidGeckoEvent::NATIVE_POKE: NativeEventCallback(); break; case AndroidGeckoEvent::SENSOR_EVENT: { mPendingSensorEvents = false; InfallibleTArray<float> values; mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); switch (type) { case hal::SENSOR_ORIENTATION: values.AppendElement(curEvent->X()); values.AppendElement(-curEvent->Y()); values.AppendElement(-curEvent->Z()); break; case hal::SENSOR_ACCELERATION: case hal::SENSOR_LINEAR_ACCELERATION: case hal::SENSOR_GYROSCOPE: values.AppendElement(-curEvent->X()); values.AppendElement(curEvent->Y()); values.AppendElement(curEvent->Z()); break; case hal::SENSOR_PROXIMITY: values.AppendElement(curEvent->X()); break; default: __android_log_print(ANDROID_LOG_ERROR, "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d", type); } const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState(); hal::SensorData sdata(type, PR_Now(), values, accuracy); hal::NotifySensorChange(sdata); } break; case AndroidGeckoEvent::LOCATION_EVENT: { if (!gLocationCallback) break; nsGeoPosition* p = curEvent->GeoPosition(); if (p) gLocationCallback->Update(curEvent->GeoPosition()); else NS_WARNING("Received location event without geoposition!"); break; } case AndroidGeckoEvent::ACTIVITY_STOPPING: { if (curEvent->Flags() > 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); obsServ->NotifyObservers(nsnull, "memory-pressure", minimize.get()); obsServ->NotifyObservers(nsnull, "application-background", nsnull); break; } case AndroidGeckoEvent::ACTIVITY_SHUTDOWN: { nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(context, "shutdown-persist"); obsServ->NotifyObservers(nsnull, "quit-application-granted", nsnull); obsServ->NotifyObservers(nsnull, "quit-application-forced", nsnull); obsServ->NotifyObservers(nsnull, "profile-change-net-teardown", context.get()); obsServ->NotifyObservers(nsnull, "profile-change-teardown", context.get()); obsServ->NotifyObservers(nsnull, "profile-before-change", context.get()); nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1"); if (appSvc) appSvc->Quit(nsIAppStartup::eForceQuit); break; } case AndroidGeckoEvent::ACTIVITY_PAUSING: { if (curEvent->Flags() == 0) { // We aren't transferring to one of our own activities, so set // background status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-background", nsnull); } // We really want to send a notification like profile-before-change, // but profile-before-change ends up shutting some things down instead // of flushing data nsIPrefService* prefs = Preferences::GetService(); if (prefs) { // reset the crash loop state nsCOMPtr<nsIPrefBranch> prefBranch; prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch)); if (prefBranch) prefBranch->SetIntPref("recent_crashes", 0); prefs->SavePrefFile(nsnull); } break; } case AndroidGeckoEvent::ACTIVITY_START: { if (curEvent->Flags() > 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); break; } case AndroidGeckoEvent::SCREENSHOT: { if (!mBrowserApp) break; AndroidBridge* bridge = AndroidBridge::Bridge(); if (!bridge) break; nsCOMPtr<nsIDOMWindow> domWindow; nsCOMPtr<nsIBrowserTab> tab; float scale; mBrowserApp->GetBrowserTab(curEvent->MetaState(), getter_AddRefs(tab)); if (!tab) break; tab->GetWindow(getter_AddRefs(domWindow)); if (!domWindow) break; if (NS_FAILED(tab->GetScale(&scale))) break; nsTArray<nsIntPoint> points = curEvent->Points(); NS_ASSERTION(points.Length() == 2, "Screenshot event does not have enough coordinates"); bridge->TakeScreenshot(domWindow, 0, 0, points[0].x, points[0].y, points[1].x, points[1].y, curEvent->MetaState(), scale); break; } case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { if (curEvent->Characters().Length() == 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); const NS_ConvertUTF16toUTF8 topic(curEvent->Characters()); const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra()); obsServ->NotifyObservers(nsnull, topic.get(), data.get()); break; } case AndroidGeckoEvent::LOAD_URI: { nsCOMPtr<nsICommandLineRunner> cmdline (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); if (!cmdline) break; if (curEvent->Characters().Length() == 0) break; char *uri = ToNewUTF8String(curEvent->Characters()); if (!uri) break; const char *argv[3] = { "dummyappname", "-remote", uri }; nsresult rv = cmdline->Init(3, const_cast<char **>(argv), nsnull, nsICommandLine::STATE_REMOTE_AUTO); if (NS_SUCCEEDED(rv)) cmdline->Run(); nsMemory::Free(uri); break; } case AndroidGeckoEvent::SIZE_CHANGED: { // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event if (curEvent != gLastSizeChange) { gLastSizeChange = new AndroidGeckoEvent(curEvent); } nsWindow::OnGlobalAndroidEvent(curEvent); break; } case AndroidGeckoEvent::VISITED: { #ifdef MOZ_ANDROID_HISTORY nsAndroidHistory::NotifyURIVisited(nsString(curEvent->Characters())); #endif break; } case AndroidGeckoEvent::NETWORK_CHANGED: { hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->Bandwidth(), curEvent->CanBeMetered())); break; } case AndroidGeckoEvent::ACTIVITY_RESUMING: { if (curEvent->Flags() == 0) { // We didn't return from one of our own activities, so restore // to foreground status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nsnull, "application-foreground", nsnull); } break; } case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: { hal::NotifyScreenOrientationChange(static_cast<dom::ScreenOrientation>(curEvent->ScreenOrientation())); break; } default: nsWindow::OnGlobalAndroidEvent(curEvent); } EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type()); return true; }
/* void parentChanged (in nsIDOMElement newParent); */ NS_IMETHODIMP xgGtkElement::ParentChanged (nsIDOMElement *newParent) { /* no need for this rigamarole... */ if (GTK_IS_WINDOW (mObject)) { return NS_OK; } /* just as a sanity check */ nsCOMPtr<xgIGObjectHolder> wrappedParent (do_QueryInterface (newParent)); GObject *parent = NULL; if (!wrappedParent || NS_FAILED (wrappedParent->GetGObject (&parent)) || !GTK_IS_CONTAINER (parent)) { g_warning (GOM_LOC ("Could not get parent widget for node: <%s>"), G_OBJECT_TYPE_NAME (mObject)); return NS_ERROR_FAILURE; } g_assert (GTK_WIDGET (parent) == gtk_widget_get_parent (GTK_WIDGET (mObject))); nsresult rv; nsCOMPtr<nsIDOMElement> elem; rv = mWrapper->GetElementNode (getter_AddRefs (elem)); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr<nsIDOMNamedNodeMap> attrs; rv = elem->GetAttributes (getter_AddRefs (attrs)); NS_ENSURE_SUCCESS (rv, rv); PRUint32 len; rv = attrs->GetLength (&len); NS_ENSURE_SUCCESS (rv, rv); nsCOMPtr<nsIDOMNode> attr; nsAutoString attrName; nsAutoString attrValue; const char *camel_prop; GValue gval = { 0 }; for (PRUint32 i = 0; i < len; i++) { rv = attrs->Item (i, getter_AddRefs (attr)); NS_ENSURE_SUCCESS (rv, rv); rv = attr->GetNodeName (attrName); NS_ENSURE_SUCCESS (rv, rv); NS_ConvertUTF16toUTF8 attrCName (attrName); camel_prop = gom_camel_uncase (attrCName.get ()); rv = attr->GetNodeValue (attrValue); NS_ENSURE_SUCCESS (rv, rv); g_value_init (&gval, G_TYPE_STRING); g_value_set_string (&gval, NS_ConvertUTF16toUTF8 (attrValue).get ()); append_child_attrs_foreach ((gpointer)camel_prop, (gpointer)&gval, (gpointer)mObject); GOM_CAMEL_FREE (camel_prop, attrCName.get ()); g_value_unset (&gval); } return NS_OK; }
bool nsAppShell::ProcessNextNativeEvent(bool mayWait) { EVLOG("nsAppShell::ProcessNextNativeEvent %d", mayWait); SAMPLE_LABEL("nsAppShell", "ProcessNextNativeEvent"); nsAutoPtr<AndroidGeckoEvent> curEvent; { MutexAutoLock lock(mCondLock); curEvent = PopNextEvent(); if (!curEvent && mayWait) { SAMPLE_LABEL("nsAppShell::ProcessNextNativeEvent", "Wait"); // hmm, should we really hardcode this 10s? #if defined(DEBUG_ANDROID_EVENTS) PRTime t0, t1; EVLOG("nsAppShell: waiting on mQueueCond"); t0 = PR_Now(); mQueueCond.Wait(PR_MillisecondsToInterval(10000)); t1 = PR_Now(); EVLOG("nsAppShell: wait done, waited %d ms", (int)(t1-t0)/1000); #else mQueueCond.Wait(); #endif curEvent = PopNextEvent(); } } if (!curEvent) return false; EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type()); switch (curEvent->Type()) { case AndroidGeckoEvent::NATIVE_POKE: NativeEventCallback(); break; case AndroidGeckoEvent::SENSOR_EVENT: { InfallibleTArray<float> values; mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); switch (type) { case hal::SENSOR_ORIENTATION: case hal::SENSOR_LINEAR_ACCELERATION: case hal::SENSOR_ACCELERATION: case hal::SENSOR_GYROSCOPE: case hal::SENSOR_PROXIMITY: values.AppendElement(curEvent->X()); values.AppendElement(curEvent->Y()); values.AppendElement(curEvent->Z()); break; case hal::SENSOR_LIGHT: values.AppendElement(curEvent->X()); break; default: __android_log_print(ANDROID_LOG_ERROR, "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d", type); } const hal::SensorAccuracyType &accuracy = (hal::SensorAccuracyType) curEvent->MetaState(); hal::SensorData sdata(type, PR_Now(), values, accuracy); hal::NotifySensorChange(sdata); } break; case AndroidGeckoEvent::LOCATION_EVENT: { if (!gLocationCallback) break; nsGeoPosition* p = curEvent->GeoPosition(); if (p) gLocationCallback->Update(curEvent->GeoPosition()); else NS_WARNING("Received location event without geoposition!"); break; } case AndroidGeckoEvent::ACTIVITY_STOPPING: { if (curEvent->Flags() > 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(minimize, "heap-minimize"); obsServ->NotifyObservers(nullptr, "memory-pressure", minimize.get()); obsServ->NotifyObservers(nullptr, "application-background", nullptr); break; } case AndroidGeckoEvent::ACTIVITY_SHUTDOWN: { nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); NS_NAMED_LITERAL_STRING(context, "shutdown-persist"); obsServ->NotifyObservers(nullptr, "quit-application-granted", nullptr); obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr); obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get()); obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get()); obsServ->NotifyObservers(nullptr, "profile-before-change", context.get()); obsServ->NotifyObservers(nullptr, "profile-before-change2", context.get()); nsCOMPtr<nsIAppStartup> appSvc = do_GetService("@mozilla.org/toolkit/app-startup;1"); if (appSvc) appSvc->Quit(nsIAppStartup::eForceQuit); break; } case AndroidGeckoEvent::ACTIVITY_PAUSING: { if (curEvent->Flags() == 0) { // We aren't transferring to one of our own activities, so set // background status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nullptr, "application-background", nullptr); // If we are OOM killed with the disk cache enabled, the entire // cache will be cleared (bug 105843), so shut down the cache here // and re-init on resume if (nsCacheService::GlobalInstance()) nsCacheService::GlobalInstance()->Shutdown(); } // We really want to send a notification like profile-before-change, // but profile-before-change ends up shutting some things down instead // of flushing data nsIPrefService* prefs = Preferences::GetService(); if (prefs) { // reset the crash loop state nsCOMPtr<nsIPrefBranch> prefBranch; prefs->GetBranch("browser.sessionstore.", getter_AddRefs(prefBranch)); if (prefBranch) prefBranch->SetIntPref("recent_crashes", 0); prefs->SavePrefFile(nullptr); } break; } case AndroidGeckoEvent::ACTIVITY_START: { if (curEvent->Flags() > 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nullptr, "application-foreground", nullptr); break; } case AndroidGeckoEvent::THUMBNAIL: { if (!mBrowserApp) break; AndroidBridge* bridge = AndroidBridge::Bridge(); if (!bridge) break; int32_t tabId = curEvent->MetaState(); const nsTArray<nsIntPoint>& points = curEvent->Points(); RefCountedJavaObject* buffer = curEvent->ByteBuffer(); nsCOMPtr<ThumbnailRunnable> sr = new ThumbnailRunnable(mBrowserApp, tabId, points, buffer); MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableMethod(sr.get(), &ThumbnailRunnable::Run)); break; } case AndroidGeckoEvent::VIEWPORT: case AndroidGeckoEvent::BROADCAST: { if (curEvent->Characters().Length() == 0) break; nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); const NS_ConvertUTF16toUTF8 topic(curEvent->Characters()); const nsPromiseFlatString& data = PromiseFlatString(curEvent->CharactersExtra()); obsServ->NotifyObservers(nullptr, topic.get(), data.get()); break; } case AndroidGeckoEvent::LOAD_URI: { nsCOMPtr<nsICommandLineRunner> cmdline (do_CreateInstance("@mozilla.org/toolkit/command-line;1")); if (!cmdline) break; if (curEvent->Characters().Length() == 0) break; char *uri = ToNewUTF8String(curEvent->Characters()); if (!uri) break; char *flag = ToNewUTF8String(curEvent->CharactersExtra()); const char *argv[4] = { "dummyappname", "-url", uri, flag ? flag : "" }; nsresult rv = cmdline->Init(4, argv, nullptr, nsICommandLine::STATE_REMOTE_AUTO); if (NS_SUCCEEDED(rv)) cmdline->Run(); nsMemory::Free(uri); if (flag) nsMemory::Free(flag); break; } case AndroidGeckoEvent::SIZE_CHANGED: { // store the last resize event to dispatch it to new windows with a FORCED_RESIZE event if (curEvent != gLastSizeChange) { gLastSizeChange = new AndroidGeckoEvent(curEvent); } nsWindow::OnGlobalAndroidEvent(curEvent); break; } case AndroidGeckoEvent::VISITED: { #ifdef MOZ_ANDROID_HISTORY nsCOMPtr<IHistory> history = services::GetHistoryService(); nsCOMPtr<nsIURI> visitedURI; if (history && NS_SUCCEEDED(NS_NewURI(getter_AddRefs(visitedURI), nsString(curEvent->Characters())))) { history->NotifyVisited(visitedURI); } #endif break; } case AndroidGeckoEvent::NETWORK_CHANGED: { hal::NotifyNetworkChange(hal::NetworkInformation(curEvent->Bandwidth(), curEvent->CanBeMetered())); break; } case AndroidGeckoEvent::ACTIVITY_RESUMING: { if (curEvent->Flags() == 0) { // If we are OOM killed with the disk cache enabled, the entire // cache will be cleared (bug 105843), so shut down cache on pause // and re-init here if (nsCacheService::GlobalInstance()) nsCacheService::GlobalInstance()->Init(); // We didn't return from one of our own activities, so restore // to foreground status nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService(); obsServ->NotifyObservers(nullptr, "application-foreground", nullptr); } break; } case AndroidGeckoEvent::SCREENORIENTATION_CHANGED: { nsresult rv; nsCOMPtr<nsIScreenManager> screenMgr = do_GetService("@mozilla.org/gfx/screenmanager;1", &rv); if (NS_FAILED(rv)) { NS_ERROR("Can't find nsIScreenManager!"); break; } nsIntRect rect; int32_t colorDepth, pixelDepth; dom::ScreenOrientation orientation; nsCOMPtr<nsIScreen> screen; screenMgr->GetPrimaryScreen(getter_AddRefs(screen)); screen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height); screen->GetColorDepth(&colorDepth); screen->GetPixelDepth(&pixelDepth); orientation = static_cast<dom::ScreenOrientation>(curEvent->ScreenOrientation()); hal::NotifyScreenConfigurationChange( hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth)); break; } case AndroidGeckoEvent::NOOP: break; default: nsWindow::OnGlobalAndroidEvent(curEvent); break; } if (curEvent->AckNeeded()) { AndroidBridge::Bridge()->AcknowledgeEvent(); } EVLOG("nsAppShell: -- done event %p %d", (void*)curEvent.get(), curEvent->Type()); return true; }